一、线程状态
1、线程的六种状态
java.lang.Thread.State
1、New:尚未启动的线程的线程状态
2、Runnable:可运行线程的线程状态,等待CPU调度
3、Blocked:线程阻塞等待监视器锁定的线程状态。
处于synchronized同步代码块或方法中被阻塞。
4、Waiting:等待线程的线程状态。下列不带超时的方式:
Object.wait、Thread.join、LockSupport.park
5、Timed Waiting:具有指定等待时间的等待线程的线程状态。下列带超时的方式:
Thread.sleep、Object.wait、Thread.join、LockSupport.parkNanos、LockSupport.parkUntil
6、Terminated:终止线程的线程状态。线程正常完成执行或者出现异常。
2、线程状态直接的切换
二、线程终止
正确的线程终止——interrupt
如果目标线程在调用Object class的wait()、wait(long)或wait(long, int)方法、join()、join(long, int)或sleep(long, int)方法时被阻塞,那么Interrupt会生效,该线程的中断状态将被清除,抛出InterruptedException异常。
如果目标线程是被I/O或者NIO中的channel所阻塞,同样,I/O操作会被中断或者返回特殊异常值。达到终止线程的目的。
如果以上条件都不满足,则会设置此线程的中断状态。
正确的线程终止——标志位
在代码逻辑中,增加一个判断,用来控制线程的终止。
三、线程通信
通信的方式
想要实现多个线程之间的协同,如:线程执行先后顺序、获取某个线程执行的结果等等。
涉及到线程之间的相互通信,分为下面四类:
1、文件共享
2、网络共享
3、共享变量
4、jdk提供的线程协调API
1、文件共享


2、网络共享
3、变量共享
4、线程协作 - JDK API ★
JDK中对于需要多线程写作完成某一任务的场景,提供了对应API支持。
多线程协作的典型场景是:生产者-消费者模型。(线程阻塞、线程唤醒)
示例:线程1去买包子,没有包子,则不再执行。线程2生产出包子,通知线程1继续执行。
(1)被弃用的suspend和resume
作用:调用suspend挂起目标线程,通过resume可以恢复线程执行。
用法:
//包子店public static Object baozidian = null;public void suspendResumeTest() throws InterruptedException {//启动线程Thread consumerThread = new Thread(()-> {if(baozidian == null) {System.out.println("1、没包子,进入等待");Thread.currentThread().suspend();}System.out.println("2、买到包子,回家");});consumerThread.start();//3秒后,生成一个包子Thread.sleep(3000);baozidian = new Object();consumerThread.resume();System.out.println("3、通知消费者");}
被弃用的主要原因是,suspend()方法调用后,不会自动释放锁,容易写出死锁的代码。
所以用wait/notify和park/unpark机制对它进行替换
suspend和resume死锁示例
1)同步代码中使用
public void suspendResumeDeadLockTest() throws InterruptedException {Thread consumerThread = new Thread(() -> {if(baozidian == null) {System.out.println("1、没包子,进入等待");synchronized (this) {//当前线程拿到锁,然后挂起Thread.currentThread().suspend();}}System.out.println("2、买到包子,回家");});consumerThread.start();Thread.sleep(3000);baozidian = new Object();synchronized (this) {consumerThread.resume();}System.out.println("3、通知消费者");}
2)suspend比resume后执行
public void suspendResumeDeadLockTest2() throws InterruptedException {Thread consumerThread = new Thread(() -> {if(baozidian == null) {System.out.println("1、没包子,进入等待");try { //为这个线程加上一点延时Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}//这里挂起的执行在resume后面Thread.currentThread().suspend();}System.out.println("2、买到包子,回家");});consumerThread.start();//3秒后,生成一个包子Thread.sleep(3000);baozidian = new Object();consumerThread.resume();System.out.println("3、通知消费者");}
(2)wait/notify机制
这些方法只能由同一对象锁的持有者线程调用,也就是写在同步块里面,否则会抛出IllegalMonitorStateException异常。
wait方法导致当前线程等待,加入该对象的等待集合中,并且放弃当前持有的对象锁。notify/notifyAll方法唤醒一个或所有正在等待这个对象锁的线程。
注意:虽然wait会自动解锁,但是对顺序有要求,如果在notify被调用之后,才开始wait方法的调用,线程会永远处于WAITING状态。
用法:
public void waitNotifyTest() throws InterruptedException {new Thread(() -> {if(baozidian == null) {synchronized (this) {System.out.println("1、没包子,进入等待");try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}}System.out.println("2、买到包子,回家");}).start();//3秒之后,生产一个包子Thread.sleep(3000);baozidian = new Object();synchronized (this) {this.notifyAll();System.out.println("3、通知消费者");}}
死锁示例:
public void waitNotifyDeadLockTest() throws InterruptedException {new Thread(() -> {if(baozidian == null) {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (this) {System.out.println("1、没包子,进入等待");try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}}System.out.println("2、买到包子,回家");}).start();//3秒之后,生产一个包子Thread.sleep(3000);baozidian = new Object();synchronized (this) {this.notifyAll();System.out.println("3、通知消费者");}}
(3)park/unpark机制
线程调用park则等待“许可”,unpark方法为指定线程提供“许可(permit)”。
不要求park和unpark方法的调用顺序。
**
多次调用unpark之后,再调用park,线程会直接运行,但不会叠加,也就是说,连续多次调用park方法,第一次会拿到“许可”直接运行,后续调用会进入等待。
用法:
public void parkUnparkTest() throws InterruptedException {Thread consumerThread = new Thread(() -> {if(baozidian == null) {System.out.println("1、没包子,进入等待");LockSupport.park();}System.out.println("2、买到包子,回家");});consumerThread.start();//3秒之后,生产一个包子Thread.sleep(3000);baozidian = new Object();LockSupport.unpark(consumerThread);System.out.println("3、通知消费者");}
注意:同步代码容易写出死锁代码
死锁示例:
public void parkUnparkDeadLockTest() throws InterruptedException {Thread consumerThread = new Thread(() -> {if(baozidian == null) {synchronized (this) {System.out.println("1、没包子,进入等待");LockSupport.park();}}System.out.println("2、买到包子,回家");});consumerThread.start();//3秒之后,生产一个包子Thread.sleep(3000);baozidian = new Object();synchronized (this) {LockSupport.unpark(consumerThread);System.out.println("3、通知消费者");}}
伪唤醒
警告!之前代码中用if语句来判断是否进入等待状态是错误的!
官方建议应该在循环中检查等待条件,原因是处于等待状态的线程可能会收到错误警报和伪唤醒**,如果不在循环中检查等待条件,程序就会再没有满足结束条件的情况下退出。
伪唤醒是指线程并非因为notify、notifyAll、unpark等api调用而唤醒,是更底层原因导致的。
四、线程池
1、为什么要用线程池
2、线程池原理
3、线程池API




