多线程基础
一、线程运行状态
Java内存模型(JMM)是围绕着多线程的原子性、可见性、有序性来建立的。
原子性:是指一个操作是不可中断的。java内存模型直接保证的原子性操作包括read、load、assign、use、store、write,synchronized块之间的操作也具备原子性。
可见性:是指当一个线程修改了某一个共享变量的值,其他线程是能立即知道这个修改。volatile、synchronized和final能够实现可见性。普通变量与volatile变量的区别是,volatile保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。final关键字的可见性是指:被final修饰的字段在构造函数中一旦初始化完成,并且构造器没有把“this”的引用传递出去,那在其他线程中就能看见final字段的值。
有序性:有序性问题的原因是因为程序在执行时,可能会进行指令重排,重排后的指令与原指令的顺序未必一致。volatile、synchronized能保证线程之间的有序性。
二、Thread类方法解析
2.1 start()方法(不是run()方法)
start()方法会新建一个线程并让这个线程执行run()方法。
2.2 stop()方法
一般来说,线程执行完毕后就会结束。stop方法会直接终止一个线程,并且会立即释放这个线程所持有的锁。太过于暴力,可能会引起数据不一致的问题,不建议使用。
2.3 sleep(long millis
)
让线程休眠多少毫秒,让出CPU资源。当休眠结束后,不一定立即获取CPU执行,是由阻塞进入就绪,重新竞争CPU。
注意:当线程休眠时,如果调用interrupt()方法会产生一个中断异常InterruptedException。
2.4 线程中断 interrupt()
线程中断并不会使线程立即退出,而是给线程发送一个通知,告知线程可以退出了,至于线程怎么处理,完全有线程决定。若设置了中断标志,但是线程没有判断中断标志并处理,线程也不会退出。
interrupt() //设置线程中断标志
isInterrupted() // 判断是否被中断
interrupted() // 判断是否被中断,并清除中断标志
public static void main(String[] args) { Runnable target = new Runnable() { @Override public void run() { while(true){ System.out.println(Thread.currentThread().getName()+" runing "); if(Thread.currentThread().isInterrupted()){ System.out.println("当前线程被设置的中断标志,手动结束线程"); break; } try { Thread.sleep(2000); //if(出现某种错误){ //当出现某种错误,需要当前线程退出时,可以设置线程中断 //Thread.currentThread().interrupt(); //} System.out.println("线程是否设置中断标志:"+Thread.currentThread().isInterrupted()); } catch (InterruptedException e) { System.out.println("在休眠的时候中断操作,抛出中断异常,线程是否设置中断标志:"+Thread.currentThread().isInterrupted()); Thread.currentThread().interrupt(); } } } }; Thread t1 = new Thread(target); t1.start(); }
2.5 等待(wait)和通知(notify)
这两个方法不是Thread类中的,是Object类中的。
如果一个线程调用了object.wait(),那么它就进入object对象的等待队列,等待被唤醒。在等待队列中,可能会有多个线程,当object.notify()被调用时,会随机选择一个线程,将其唤醒,notifyAll()方法会唤醒所有的线程。
注意:wait和sleep一样,在wait时发生中断,也会抛出中断异常
2.6 挂起(suspend)和继续执行(resume)
被废弃,不推荐使用。因为suspend在导致线程暂停的同时,并不会去释放任何资源。其他线程想要访问被它暂用的锁时,导致无法继续运行。直到进行了resume操作,才能继续。可以使用wait和notify实现其功能。
2.7 等待线程结束(jion)和谦让(yield)
join()
jion(long millis)
yield() 方法会让当前线程让出CPU,进入就绪状态,但还会进行CPU的争夺。
第一个jion表示无限等待,它会一直阻塞当前线程,直到目标线程(谁调用jion谁是目标线程)执行完毕。第二个join给出了一个最大等待时间,如果超过给定时间目标线程还在执行,当前线程也会继续往下执行。
Thread t0 = new Thread(()->{ for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+"执行"); } }); Thread t1 = new Thread(()->{ for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+"执行"); } }); t0.start(); t1.start(); try { t0.join(); t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("主线程执行");
当没有执行t0.jion()和t1.jion()结果如下:
可以看到主线程抢到CPU先执行,然后t0和t1交替执行。
当执行t0.jion()和t1.jion()结果如下:
可以看到是t0和t1先交替执行,然后主线程再执行,所以当目标线程加上join后,主线程会等目标线程执行结束后再执行。
注意:当仅仅执行t0.jion()时,效果也是这样,join阻塞的是当前线程,其他线程不干扰。其原理是目标线程让当前线程(只是当前线程)进行wait()等待,当目标线程执行完成后,被等待的线程会在退出前调用notifyAll()唤醒所有线程。
2.8 守护线程
守护线程是系统的守护者,在后台默默完成一些服务,当用户线程全部结束后,守护线程也会结束。
Thread t0 = new Thread(target); t0.setDaemon(true); t0.start(); // 主线程的操作 ...
当前线程只有用户线程(主线程)和t0,t0设置为守护线程,当主线程执行结束后,t0也会结束;若不设置的话,t0会一直执行。
本文来自博客园,作者:wzyy,转载请注明原文链接:https://www.cnblogs.com/wwzyy/p/10090992.html