——- android培训、java培训、期待与您交流! ———-
1、 进程、线程、多线程的区别:
1)进程:是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该循序是一个执行路径,或叫控制单元。
2)线程:是进程中的一个独立的控制单元,线程在控制着进程的执行。
3)多线程:一个进程中可以有多个执行路径。
注意:一个进程中至少要有一个线程。
JVM启动时启动了多条线程,至少有两个线程可以分析的出来:
1.执行main函数的线程,该线程的任务代码都定义在main函数中。
2.负责垃圾回收的线程。
2、 创建线程的两种方法:
第一种方法:继承Thread类。
步骤:1)定义类继承Thread类
2)复写Thread类中的run方法
3)直接创建Thread的子类对象创建线程
4)调用线程的start方法(作用:启动线程;调用run方法)
class MyThread extends Thread
{
public void run()
{
//要在线程中执行的代码
for(int i=0;i<100;i++)
System.out.println("MyThread:"+i);
}
}
第二种方法:实现Runnable接口。
步骤:1)定义类实现Runnable接口
2)覆盖Runnable接口中的run方法
3)通过Thread类建立线程对象
4)将Runnable接口的子类对象作为实际参数传给Thread类的构造函数
5)调用Thread类的start方法开启线程并调用Runnable接口子类的run方法
class MyThread1 implements Runnable
{
public void run()
{
//要在线程中执行的代码
for(int i=0;i<100;i++)
System.out.println("MyThread:"+i);
}
}
完整代码示例演示:
package com.shan;
/**需求:建立一个多线程
* @param args
*/
public class Demo37 {
public static void main(String[] args)
{
System.out.println("启动主线程");
//启动第一个线程
Thread t1=new MyThread();
System.out.println("启动第一个线程");
t1.start();
//启动第二个线程
MyThread1 mt=new MyThread1();
Thread t2=new Thread(mt);
System.out.println("启动第二个线程");
t2.start();
}
}
class MyThread extends Thread //创建线程的第一种方式
{
public void run()
{
//要在线程中执行的代码
for(int i=0;i<100;i++)
System.out.println("MyThread:"+i);
}
}
class MyThread1 implements Runnable //创建线程的第二种方式
{
public void run()
{
//要在线程中执行的代码
for(int i=0;i<100;i++)
System.out.println("MyThread1:"+i);
}
}
3、 实现方式和继承方式有什么区别?(重点)
1) 实现方式好处在于避免了单继承的局限性,在定义线程时,建议使用实现方式
2) 线程代码存放的位置不一样
继承Thread类:线程代码存放在Thread子类的run方法中。
实现Runnable接口:线程代码存放在接口的子类的run方法中
4、 线程都有自己默认的名称。Thread-编号(该编号从0开始)
Static Thread currentThread():获取当前线程对象
getName():获取线程名称
设置线程名称:setName或者构造函数
5、线程安全问题产生的原因:
1.多个线程在操作共享的数据。
2.操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。
解决方案:
1)同步代码块:(java对于多线程的安全问题提供了专业的解决方式,就是同步代码块)
格式:synchronized(对象)
{需要被同步的代码;}
2) 同步函数:
格式:在函数上加上synchronized修饰符即可。
示例演示:模拟4个线程同时卖100张票
package com.shan;
/**
* 需求:模拟4个线程同时卖100张票。
* @author ling
*
*/
public class Demo38 {
public static void main(String[] args)
{
Ticket t=new Ticket();
Thread t1 =new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Ticket implements Runnable
{
private int num =1000;
Object obj=new Object();
public void run()
{
while(true)
{
synchronized(obj)
{
if(num>0)
{
System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
}
}
}
}
}
6、 同步的前提:
1) 必须要有两个或两个以上的线程;
2) 必须保证同步中只有一个线程在运行;
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源。
7、 同步有两种表现形式:
一种是同步代码块;一种是同步函数
8、 同步函数使用的锁是this,静态的同步函数使用的锁是该方法所在类的字节码文件对象,即:类名.class
示例演示:储户,两个,每个都到银行存钱,每次存100,共存三次。
classBank{
privateintsum;
publicsynchronizedvoidadd(intnum){//同步函数
sum=sum+num;
System.out.println("sum="+sum);
}
}
classCusimplementsRunnable{
privateBankb=newBank();
publicvoidrun(){
for(intx=0;x<3;x++){
b.add(100);
}
}
}
classBankDemo{
publicstaticvoidmain(String[]args){
Cusc=newCus();
Threadt1=newThread(c);
Threadt2=newThread(c);
t1.start();
t2.start();
}
}
单例模式中的懒汉式就用到了同步函数
class Single{
private static Single s = null;
private Single(){}
public static Single getInstance(){
if(s ==null){
synchronized(Single.class){
if(s == null)
s = new Single();
}
}
return s ;
}
}
9、 死锁:同步中嵌套同步,就出现死锁现象。
10、线程间的通信:(等待唤醒机制)
1)操作线程的方法有:wait()/notify()/notifyAll();
为什么这些操作线程的方法要定义在Object类中呢?
分析:因为这些方法在操作同步中的线程时,都必须要标识它们所操作线程特有的锁,只有同一个锁上的被等待线程可以被同一个锁上的notify唤醒,不可以对不同锁中的线程唤醒。也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。
2) wait()和sleep()有什么区别?
分析:wait():释放资源释放锁,没有执行权。
sleep():释放资源不释放锁,有执行权。
示例演示:生产者—消费者
class Resource{
private String name ;
private String sex ;
private boolean flag = false;
public synchronized void set(String name,String sex){
if(flag )
try{
this.wait();
} catch(InterruptedException e){
e.printStackTrace();
}
this.name = name;
this.sex = sex;
flag = true ;
this.notify();
}
public synchronized void out(){
if(!flag )
try{
this.wait();
} catch(InterruptedException e){
e.printStackTrace();
}
System. out.println(name + "..." + sex);
flag = false ;
this.notify();
}
}
class Input implements Runnable{
Resource r;
Input(Resource r){
this.r = r;
}
public void run(){
int x = 0;
while(true ){
if(x == 0){
r.set( "mike","男" );
} else{
r.set( "lili","女" );
}
x = (x + 1)%2;
}
}
}
//输出
class Output implements Runnable{
Resource r;
Output(Resource r){
this.r = r;
}
public void run(){
while(true ){
r.out();
}
}
}
class ResourceDemo {
public static void main(String[] args){
//创建资源
Resource r = new Resource();
//创建任务
Input in = new Input(r);
Output out = new Output(r);
//创建线程,执行路径
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
//开启线程
t1.start();
t2.start();
}
}
11、 停止线程
1) 定义循环结束标记。
因为线程运行代码一般都是循环,只要控制了循环即可。
2) 使用interrupt(中断)方法
该方法是结束线程的冻结状态,是线程回到运行状态中来。
总结:如何停止线程?
只有一种,run方法结束。 开启多线程运行,运行代码通常是循环结构。 只要控制住循环,就可以让run方法结束,也就是线程结束。
12、 守护线程
setDaemon()方法是将该线程标记为守护线程或用户线程(后台线程)。当正在运行的线程都是守护线程时,java虚拟机自动退出。
该方法必须在启动线程前调用。
判断是否是后台线程:使用Thread对象的isDaemon()方法;
例如:
StopThread st=new StopThread ();
Thread t1=new Thread (st);
Thread t2=new Thread (st);
t1.setDaemon(true); //如果为true,则表示将该线程标记为守护线程
t2.setDaemon(true);
t1.start();
t2.start();
13、 join方法:
调用join方法的线程对象强制运行,该线程强制运行期间,其他线程无法运行,必须等到该线程结束后其他线程才可以运行。
join方法的重载方法:
join(long millis):
join(long millis,int nanos):
14、 线程的优先级:
每个线程都有优先级,优先级的高低只和线程获得执行机会的次数多少有关。
并非线程优先级越高的就一定先执行,哪个线程的先运行取决于CPU的调度;
默认情况下main线程具有普通的优先级,而它创建的线程也具有普通优先级。
Thread对象的setPriority(int x)和getPriority()来设置和获得优先级。
MAX_PRIORITY : 值是10
MIN_PRIORITY : 值是1
NORM_PRIORITY : 值是5(主方法默认优先级)
15、 线程让步:(yield)
暂停当前正在执行的线程对象,把执行机会让给相同或更高优先级的其他线程,让线程可以你一个我一个的进行交替执行。
——- android培训、java培训、期待与您交流! ———-