线程的生命周期在线剖析
1.NEW(新建)--->RUNNABLE(就绪、运行)
Java层:创建Thread对象,此时是普通的Java对象没有于操作系统底层线程关联。
JVM层:调用start方法时,JVM创建javaThread对象调用pthread_create创建OS线程
OS层:分配线程控制块,线程栈等资源,线程进入就绪队列
关键点:start方法的调用涉及到Java对象和OS线程两次创建,这也是为什么线程创建成本高的原因。
2.RUNNABLE的内部duality(二象性)
核心:包含了两种OS状态。
1.Ready(就绪):线程已准备,等待OS分配CPU时间片。
2.Running(运行):获得CPU时间片,执行run方法代码。
JVM视角:
JVM维护一个可运行线程队列,OS调度器从这个队列选择线程分配CPU,对于JVM来说,无论时等待还是运行,都是“可运行的”。
性能影响:RUNNABLE--->READY(时间片用完)会发生上下文切换。
3.BLOCKED(阻塞)监视器锁的竞争---->从未持有锁
线程尝试获取被其他线程持有的监视器锁时导致进入BLOCKED状态。重要区别:只有synchronized会导致线程进入BLOCKED状态,Lock接口的锁不会。
每个Java对象都关联一个监视器锁(Monitor),JVM维护一个该锁的入口集(Enrty Set),线程在 入口处竞争失败就会进入Entry Set,状态为BLOCKED,当持锁线程释放锁时,JVM从Entry Set中随机唤醒(自动唤醒)一个线程,此时涉及到非公平锁本质,唤醒的线程不比新来的线程有优先权,唤醒的线程还需同新线程竞争锁。
4.WAITING(超时等待)--->在锁内部等待,曾经持有锁但主动释放了
进入Wait Set等待集中,需要其他线程显示调用notify/notifyAll唤醒,否则无限期等待。
Object.wait() //需要先持有锁 1.释放锁 2.进入等待集Wait Set 3.等待通知
LockSupport.park() //JUC的等待基础
Thread.join() //等待线程结束,底层也是wait()
过程:1.线程释放持有的监视器锁。2.线程被放入该对象的等待集。3.状态变为WAITING。4,其他线程调用notify/notifyAll时,从Wait Set移到Entry Set5.重新竞争。
5.TIMED_WAITING(超时等待)--->在锁内部等待,曾经持有锁但主动释放了
同上,但会在指定时间后自动唤醒。
6.TERMINATED(终止)
线程的run方法执行完毕或因异常退出,线程生命结束。
注意:此时的线程不可再次启动,会抛出illegalThreadStateException。
注意点:
wait方法必须在同步块中调用,否则抛出illegalMonitorStateException,sleep在任何地方都可以。
sleep方法不会释放锁,wait方法会主动释放锁。
不要使用stop停止线程会立即释放锁可能导致数据不一致,使用中断机制thread.interrupt()设置中断标志,线程run方法中通过isInterrupted或捕获InterruptedException来安全的结束任务。