线程状态
- New:尚未启动的线程的线程状态。
- Runnable:可运行线程的线程状态,等待CPU调度。
- Blocked:线程锁定等待监视器锁定的状态。处于synchronized同步代码块或方法中被阻塞。
- Waiting:等待线程的线程状态。下列不带超时的方式:Object.wait,Thred.join,LockSupport.park
- Timed Waiting:具有指定等待时间的等待线程的线程状态。下列带超时的等待状态:Thread.sleep,Object.wait,Thread.join,LockSupport.parkNanos,LockSupport.parkUtil。
- Terminated:终止线程的线程状态。线程正常完成或者出现异常。
线程状态切换
- 一个线程创建出来状态为New。当调用开始的时候,状态变为Runnable。如果与其他线程争抢一把锁且没有获取到锁,就会进入Blocked状态;如果拿到锁就会回到Runnable状态。
- 线程接受到wait方法或者join方法,线程状态就从Runnable切换到Waiting状态,等待其他线程通知;如果收到其他线程通知,会回到Runnable状态,否则一直是Waiting状态。
- 线程接受到有超时时间的等待方法,线程状态从Runnable状态切换到Timed Waiting状态;当等待超时或者收到其他线程通知后,会回到Runnable状态。
- 线程执行成功或者执行异常就会进入Terminated状态。
线程终止
不正确的线程中止
- Stop:中止线程,并清除监控器锁信息,但是可能导致线程安全的问题,JDK不建议使用。
正确的线程中止
- interrupt:如果目标线程在调用Object class的wait()、wait(long)或wait(long,int)方法、join()、join(long,int)或sleep(long,int)方法时被阻塞,那么interrupt会生效,线程的中断状态将被清除,抛出InterruptedException异常。如果线程是被IO或者NIO的Channel所阻塞,同样,IO操作会被中断或者返回特殊的异常值。达到终止线程的目的。如果以上条件都不满足,则会设置此线程的一个中断状态。
- 标志位:代码逻辑中,增加一个判断,用来控制线程执行的中止。
线程通信
要想实现多个线程之间的协同,如:线程执行先后顺序、获取某个线程执行结果等等。涉及到线程的相互通信,分为以下四类:
- 文件共享
- 网络共享
- 共享变量
- jdk提供的线程协调API:suspend/resume、wait/notify、park/unpark。
线程协作JDK API
JDK对于需要多线程协作完成某一任务的场景,提供了对应的API支持。多线程协作的典型场景是:生产者消费者模型(线程阻塞、线程唤醒)。
suspend/resume(已弃用)
作用:调用suspend挂起目标线程,通过resume可恢复线程执行。
被弃用的主要原因是容易写出死锁代码。首先,suspend被挂起后不会释放锁;其次,必须先执行suspend再执行resume,否则死锁。所以用wait/notify、park/unpark机制对它进行替代。
wait/notify机制
这些方法只能由同一对象锁的持有者线程调用,也就是写在同步代码块里面,否则会抛出IllegalMonitorStateException异常。wait方法导致当前线程等待,加入到对象的等待集合中,并且放弃当前持有的对象锁。notify/notifyAll方法唤醒一个或所有正在等待当前对象锁的线程。
需要注意的是,虽然wait会自动解锁,但是对顺序有要求,如果在notify后才开始wait的调用,线程会永远处于WAITING状态。
park/unpark机制
线程调用park则等待『许可』,unpark为指定线程提供『许可』。不要求调用的先后顺序。多次调用unpark后,再调用park,线程会直接运行,但不会叠加。也就是说,连续多次调用park方法,第一次会拿到许可直接运行,后续调用会进入等待。需要注意的是park方法不会释放锁。