wait和notify原理
wait和notify(重点)
小故事
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CDMyQx9F-1627541763521)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210407112231826.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1wwjn2wY-1627541763535)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210407112256160.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9La2jFk3-1627541763537)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210407112303448.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lYwjCpIz-1627541763540)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210407112313985.png)]
wait、notify介绍(必须要获取到锁对象,才能调用这些方法)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RdgVrrIK-1627541763543)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210407112351616.png)]
- 当
线程0
获取到了锁
,称为Monitor
的Owner
,但是此时它发现自己要执行synchronize代码块
的条件不满足;此时它就要调用obj.wait
方法,进入到Monitor中的waitSet
集合,此时线程0
的状态就变为WAITING
- 处于
BLOCKED
和WAITING
状态的线程都为阻塞状态
,CPU都不会分给他们时间片。但是有所区别:BLOCKED
状态的线程实在竞争锁对象
时,发现Monitor的Owner已经是别的线程了
, 此时就会进入EntryList
中,并处于BLOCKED
状态WAITING状态
的线程是获取了对象的锁
,但是自身的原因无法执行synchronize的临界区资源
需要进入阻塞状态
时,锁对象调用了wait方法而进入了WaitSet中,处于WAITING状态
- ·
处于BLOCKED状态的线程会在锁被释放的时候被唤醒
处于WAITING扎UN盖亚IDE线程只有被锁对象调用了nofity方法(obj.notify/obj.nofityAll),才会被唤醒。然后它会进入到EntryList,重新竞争锁
(此时就会将锁升级为重量级锁
)
PS:
可以参考生产者/消费者模式来进行理解。有如下代码:
package socket;
import java.util.Random;
//生产消费者
public class Test7 {
public static void main(String[] args) {
Box box=new Box();
Thread t1=new Thread(new Producer(box));
t1.setName("生产者");
Thread t2=new Thread(new Consumer(box));
t2.setName("消费者");
t1.start();
t2.start();
}
}
//生产任务类
class Producer implements Runnable{
Box box;
Producer(Box box){
this.box=box;
}
@Override
public void run() {
Random r=new Random();
for (int i = 0; i < 10; i++) {
Apple a=new Apple(i);
box.deposite(a);
try {
Thread.sleep(r.nextInt(3)*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//消费任务类
class Consumer implements Runnable{
Box box;//生产和消费操作的资源相同
Consumer(Box box){
this.box=box;
}
@Override
public void run() {
Random r=new Random();
for (int i = 0; i < 10; i++) {
Apple a=box.withdraw();
try {
Thread.sleep(r.nextInt(3)*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//容器,存放苹果
class Box{
Apple [] apples=new Apple[5];
int index=0;//容器索引
//入库
public synchronized void deposite(Apple apple){
while(index>=apples.length){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();//唤醒其他线程
apples[index]=apple;//入库
index++;
System.out.println(Thread.currentThread().getName()+"生产了"+apple);
}
//出库
public synchronized Apple withdraw(){
while (index==0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
Apple a=apples[index-1];
index--;
System.out.println(Thread.currentThread().getName()+"消费了"+a);
return a;
}
}
//苹果类
class Apple{
int id;
Apple(int id){
this.id=id;
}
@Override
public String toString() {
return "Apple{" +
"id=" + id +
'}';
}
}
API介绍
下面的三个方法都是 Object
中的方法;通过 锁对象
来调用
wait()
:让获得锁对象的线程到waitSet
中一直等待wait(long n ):
让该等待线程没有被notify,等到时间到了之后,也会自动唤醒notify():
让获得对象所的线程,使用锁对象调用notify
去waitSet的等待线程中挑一个唤醒notifyAll():
让获得对象锁的线程,使用锁对象调用notifyAll
去唤醒waitSet中所有的等待线程
它们都是 线程之间进行协作的手段
,都属于Object对象的方法
,必须要获得此对象的锁,才能调用这些方法
注:只有当对象被锁以后(称为Owner),才能调用wait和notify方法
public class Test1 {
final static Object LOCK = new Object();
public static void main(String[] args) throws InterruptedException {
//只有在对象被锁住后才能调用wait方法
synchronized (LOCK) {
LOCK.wait();
}
}
}
- 演示
wait和notify
方法
/**
* Description:
*
* @author HillCheung
* @date 2021年4月7日11:36:55
*/
@Slf4j(topic = "guizy.WaitNotifyTest")
public class WaitNotifyTest {
static final Object obj = new Object();
public static void main(String[] args) throws Exception {
new Thread(() -> {
synchronized (obj) {
log.debug("执行...");
try {
// 只有获得锁对象之后, 才能调用wait/notify
obj.wait(); // 此时t1线程进入WaitSet等待
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其它代码...");
}
}, "t1").start();
new Thread(() -> {
synchronized (obj) {
log.debug("执行...");
try {
obj.wait(); // 此时t2线程进入WaitSet等待
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其它代码...");
}
}, "t2").start();
// 让主线程等两秒在执行,为了`唤醒`,不睡的话,那两个线程还没进入waitSet,主线程就开始唤醒了
Thread.sleep(1000);
log.debug("唤醒waitSet中的线程!");
// 只有获得锁对象之后, 才能调用wait/notify
synchronized (obj) {
// obj.notify(); // 唤醒waitset中的一个线程
obj.notifyAll(); // 唤醒waitset中的全部等待线程
}
}
}
13:01:36.176 guizy.WaitNotifyTest [t1] - 执行...
13:01:36.178 guizy.WaitNotifyTest [t2] - 执行...
13:01:37.175 guizy.WaitNotifyTest [main] - 唤醒waitSet中的线程!
13:01:37.175 guizy.WaitNotifyTest [t2] - 其它代码...
13:01:37.175 guizy.WaitNotifyTest [t1] - 其它代码...
Sleep(long n) 和 Wait(long n)的区别 (重点)
不同点
Sleep是Thread类的静态方法
,Wait是Object方法
,Object又是所有类的父类,所以所有类都有Wait方法。Sleep在阻塞的时候不会释放锁
,而Wait在阻塞的时候回释放锁(不释放锁的话,其他线程就无法唤醒线程了)
- Sleep方法不需要与synchronize一起使用,而Wait方法需要与synchronize一起使用(waiy/notify等方法,必须要使用对象所来调用)
相同点:
阻塞状态
都为TIME_WAITING(显示等待)
sleep方法 / wait方法 测试
/**
* Description: 测试sleep不释放锁
*
* @author HillCheung
* @date 2020/12/20 09:33
*/
@Slf4j(topic = "guizy.SleepTest")
public class SleepTest {
public static final Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock) {
log.debug("获得锁了");
try {
// Thread.sleep(5000); // 主线程需要等5s才能获得到锁.所以所在sleep期间, 是不会释放锁的
lock.wait(5000); // 调用wait方法会立刻释放锁, 不然主线程就拿不到lock锁了, 当等待5s后程序才结束
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1").start();
// 主线程睡一秒
Sleeper.sleep(1);
synchronized (lock) {
log.debug("获得锁了");
}
}
sleep打印结果 : 表明在sleep期间, 锁是不会被释放的
wait打印结果 : 当调用wait方法后, 锁就会被立刻释放
wait/notify的使用
Step 1 : 逐渐向下优化
@Slf4j(topic = "guizy.WaitNotifyTest")
public class WaitNotifyTest {
static final Object room = new Object();
static boolean hasCigarette = false;
static boolean hasTakeout = false;
public static void main(String[] args) {
//思考下面的解决方案好不好,为什么?
new Thread(() -> {
synchronized (room) {
log.debug("有烟没?[{}]", hasCigarette);
if (!hasCigarette) {
log.debug("没烟,先歇会!");
Sleeper.sleep(2); // 会阻塞2s, 不会释放锁
}
log.debug("有烟没?[{}]", hasCigarette);
if (hasCigarette) {
log.debug("可以开始干活了");
}
}
}, "小南").start();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
synchronized (room) {
log.debug("可以开始干活了");
}
}, "其它人").start();
}
Sleeper.sleep(1);
new Thread(() -> {
// 此时没有加锁, 所以会优先于其他人先执行
// 这里能不能加 synchronized (room)?
//synchronized (room) { // 如果加锁的话, 送烟人也需要等待小南睡2s的时间,此时即使送到了,小南线程也将锁释放了..
hasCigarette = true;
log.debug("烟到了噢!");
//}
}, "送烟的").start();
}
}
- 不给
送烟线程加synchronized
输出情况:
DEBUG [小南] - 有烟没?[false]
DEBUG [小南] - 没烟,先歇会!
DEBUG [送烟的] - 烟到了噢!
DEBUG [小南] - 有烟没?[true]
DEBUG [小南] - 可以开始干活了
DEBUG [其它人] - 可以开始干活了
DEBUG [其它人] - 可以开始干活了
DEBUG [其它人] - 可以开始干活了
DEBUG [其它人] - 可以开始干活了
Step2:
@Slf4j(topic = "guizy.WaitNotifyTest")
public class WaitNotifyTest2 {
static final Object room = new Object();
static boolean hasCigarette = false;
static boolean hasTakeout = false;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
synchronized (room) {
log.debug("有烟没?[{}]", hasCigarette);
if (!hasCigarette) {
log.debug("没烟,先歇会!");
try {
room.wait(); // 此时进入到waitset等待集合, 同时会释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (hasCigarette) {
log.debug("可以开始干活了");
}
}
}, "小南").start();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
// 小南进入等待状态了, 其他线程就可以获得锁了
synchronized (room) {
log.debug("可以开始干活了");
}
}, "其它人").start();
}
Thread.sleep(1);
new Thread(() -> {
synchronized (room) {
hasCigarette = true;
log.debug("烟到了噢!");
room.notify();
}
}, "送烟的").start();
}
}
运行结果如下:
11:00:51.840 guizy.WaitNotifyTest [小南] - 有烟没?[false]
11:00:51.847 guizy.WaitNotifyTest [小南] - 没烟,先歇会!
11:00:51.847 guizy.WaitNotifyTest [其它人] - 可以开始干活了
11:00:51.847 guizy.WaitNotifyTest [其它人] - 可以开始干活了
11:00:51.847 guizy.WaitNotifyTest [其它人] - 可以开始干活了
11:00:51.847 guizy.WaitNotifyTest [其它人] - 可以开始干活了
11:00:51.847 guizy.WaitNotifyTest [其它人] - 可以开始干活了
11:00:52.847 guizy.WaitNotifyTest [送烟的] - 烟到了噢!
11:00:52.847 guizy.WaitNotifyTest [小南] - 有烟没?[true]
11:00:52.848 guizy.WaitNotifyTest [小南] - 可以开始干活了
如果此时除了小南在等待唤醒, 还有一个线程也在等待唤醒呢? 此时的notify
方法会唤醒谁呢?
Step3:
@Slf4j(topic = "guizy.WaitNotifyTest")
public class WaitNotifyTest3 {
static final Object room = new Object();
static boolean hasCigarette = false;
static boolean hasTakeout = false;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
synchronized (room) {
log.debug("有烟没?[{}]", hasCigarette);
if (!hasCigarette) {
log.debug("没烟,先歇会!");
try {
room.wait(); // 此时进入到waitset等待集合, 同时会释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("有烟没?[{}]", hasCigarette);
if (hasCigarette) {
log.debug("可以开始干活了");
}
}
}, "小南").start();
new Thread(() -> {
synchronized (room) {
log.debug("外卖送到没?[{}]", hasTakeout);
if (!hasTakeout) {
log.debug("没外卖,先歇会!");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("外卖送到没?[{}]", hasTakeout);
if (hasTakeout) {
log.debug("可以开始干活了");
} else {
log.debug("没干成活...");
}
}
}, "小女").start();
Thread.sleep(1);
new Thread(() -> {
synchronized (room) {
hasTakeout = true;
log.debug("外卖到了噢!");
room.notify();
}
}, "送外卖的").start();
}
}
运行结果如下:
DEBUG [小南] - 有烟没?[false]
DEBUG [小南] - 没烟,先歇会!
DEBUG [送外卖的] - 外卖到了噢!
DEBUG [小女] - 外卖送到没?[true]
DEBUG [小女] - 外卖送到没?[true]
DEBUG [小女] - 可以开始干活了
DEBUG [小南] - 有烟没?[false]
问题: 当外卖送到了, 却唤醒了小南
, 此时就出现了问题
new Thread(() -> {
synchronized (room) {
hasTakeout = true;
log.debug("外卖到了噢!");
room.notifyAll();
}
}, "送外卖的").start();
运行结果如下:
DEBUG [小南] - 有烟没?[false]
DEBUG [小南] - 没烟,先歇会!
DEBUG [送外卖的] - 外卖到了噢!
DEBUG [小女] - 外卖送到没?[true]
DEBUG [小女] - 外卖送到没?[true]
DEBUG [小女] - 可以开始干活了
DEBUG [小南] - 有烟没?[false]
Step5:
- 使用
while循环
来解决虚假唤醒
@Slf4j(topic = "guizy.WaitNotifyTest")
public class Main {
static final Object room = new Object();
static boolean hasCigarette = false;
static boolean hasTakeout = false;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
synchronized (room) {
log.debug("有烟没?[{}]", hasCigarette);
while (!hasCigarette) {
log.debug("没烟,先歇会!");
try {
room.wait(); // 此时进入到waitset等待集合, 同时会释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("有烟没?[{}]", hasCigarette);
if (hasCigarette) {
log.debug("可以开始干活了");
}
}
}, "小南").start();
new Thread(() -> {
synchronized (room) {
log.debug("外卖送到没?[{}]", hasTakeout);
while (!hasTakeout) {
log.debug("没外卖,先歇会!");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("外卖送到没?[{}]", hasTakeout);
if (hasTakeout) {
log.debug("可以开始干活了");
} else {
log.debug("没干成活...");
}
}
}, "小女").start();
Thread.sleep(1);
new Thread(() -> {
synchronized (room) {
hasTakeout = true;
log.debug("外卖到了噢!");
room.notifyAll();
}
}, "送外卖的").start();
}
}
运行结果如下:
DEBUG [小南] - 有烟没?[false]
DEBUG [小南] - 没烟,先歇会!
DEBUG [送外卖的] - 外卖到了噢!
DEBUG [小女] - 外卖送到没?[true]
DEBUG [小女] - 外卖送到没?[true]
DEBUG [小女] - 可以开始干活了
DEBUG [小南] - 没烟,先歇会!
因为改为while
如果唤醒之后,就在while循环中执行了,不会跑到while外面去执行"'有烟没…",此时小南就不需要每次notify,就去看是不是送来的烟,如果是烟,while就变为false了.
同步模式之保护性暂停(join、Future的实现
)
- 即
Guarded Suspension
,用在一个线程等待另外一个线程的执行结果
- 有
一个结果
需要从一个线程传递到另一个线程
,让他们 关联同一个GuardedObject - 如果 有结果不断从一个线程到另一个线程那么可以使用
消息队列
(见生产者/消费者) - JDK中,
join的实现、Future的实现
,采用的就是此模式 - 因为要等待另一方的结构,因此归类到
同步模式
- 有
-
一方等待另一方的执行结果
举例 : -
举例, 线程1等待线程2下载的结果,并获取该结果
@Slf4j(topic = "GuardeObjectTest")
public class GuardeObjectTest {
public static void main(String[] args) {
GuardObject guardObject =new GuardObject();
new Thread(()->{
log.debug("等待结果");
List<String> list = (List<String>) guardObject.get();
log.debug("结果大小:{}",list.size());
},"t1").start();
new Thread(()->{
log.debug("执行下载");
List<String> list =new ArrayList<>();;
list.add("1");
list.add("2");
guardObject.complete(list);
},"t2").start();
}
}
class GuardObject{
//结果
private Object response;
//获取结果
@SneakyThrows
public Object get(){
synchronized (this){
//防止虚假唤醒
//没有结果
while(response==null){
this.wait();
}
}
return response;
}
//产生结果
public void complete(Object response){
synchronized (this){
//给变量赋值
this.response=response;
this.notifyAll();
}
}
}
结果如下:
DEBUG [t1] - 等待结果
DEBUG [t2] - 执行下载
DEBUG [t1] - 结果大小:2
线程t1
等到线程t2
的结构,可以设置超时时间
,如果超过时间还没有返回结构,此时就不等了,退出while循环。
运行结果
// 在等待时间内的情况
16:20:41.627 guizy.GuardeObjectTest [t1] - begin
16:20:41.627 guizy.GuardeObjectTest [t2] - begin
16:20:42.633 guizy.GuardeObjectTest [t1] - 结果是:java.lang.Object@1e1d0168
// 超时的情况
16:21:24.663 guizy.GuardeObjectTest [t2] - begin
16:21:24.663 guizy.GuardeObjectTest [t1] - begin
16:21:26.667 guizy.GuardeObjectTest [t1] - 结果是:null
- 关于超时的增强,在
join(long millis) 的源码
中得到了体现:
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
// join一个指定的时间
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
- 多任务版
GuardedObject
图中Futures
就好比居民楼一楼的信箱(每个信箱有房间编号),左侧的t0,t2,t4就好比等待右键的居民,右侧的t1,t3,t5就好比邮递员如果需要多个类之间使用GuardedObject
对象,作为参数传递不是很方便,因此设计一个解耦的中间类
- 不仅能够
解耦
【结果等待者】和【结果生产者】,还能够同时支持多个任务的管理。
和生产者消费者模式的区别就是 :这个产生结构的线程
和使用结构的线程
是一一对应
的关系,但是生产者消费者模式并不是 - rpc框架的调用中就使用到了这种模式。
运行结果如下:
DEBUG [Thread-1] - 开始收信 id:1
DEBUG [Thread-3] - 送信 id:3, 内容:内容3
DEBUG [Thread-5] - 送信 id:1, 内容:内容1
DEBUG [Thread-4] - 送信 id:2, 内容:内容2
DEBUG [Thread-0] - 开始收信 id:2
DEBUG [Thread-2] - 开始收信 id:3
DEBUG [Thread-2] - 收到信 id:3, 内容:内容3
DEBUG [Thread-0] - 收到信 id:2, 内容:内容2
DEBUG [Thread-1] - 收到信 id:1, 内容:内容1
Process finished with exit code 0
异步模式之生产者/消费者(重点
)
- 与前面的
保护性暂停
中的GuardedObject
不同,不需要产生结构
和消费结构
的线程一一对应(一个生产一个消费)
消费队列
可以用来 平衡生产和消费的线程资源- 生产者仅负责产生结果数据,不关心数据该如何处理,而消费者专心处理结果数据
- 消息队列是 有
容器限制
的,满时不会再加入数据,空时不会消耗数据 - JDK中各种
阻塞队列
,采用的就是这种模式
异步模式中, 生产者产生消息之后消息没有被立刻消费
同步模式中, 消息在产生之后被立刻消费了。
- 下面例子是
线程间通信
的消息队列
,要注意区别,像RabbitMQ
等消息框架是进程间通信
的。
DEBUG [生产者0] - 已生产消息Message{id=0, value=值0}
DEBUG [消费者] - 已消费消息Message{id=0, value=值0}
DEBUG [生产者1] - 已生产消息Message{id=1, value=值1}
DEBUG [生产者2] - 已生产消息Message{id=2, value=值2}
DEBUG [消费者] - 已消费消息Message{id=1, value=值1}
DEBUG [消费者] - 已消费消息Message{id=2, value=值2}
DEBUG [消费者] - 队列为空,消费者线程等待
prak&unpack(重要)
基本使用
-
park/unpark
都是LockSupport
类中的方法 -
先调用
unpark
后,在调用park,此时park
不会暂停线程
// 暂停当前线程
LockSupport.park();
// 恢复某个线程的运行
LockSupport.unpark(thread);
原理
每个线程
都要自己的一个Parker对象
,由三部分组成_counter,_cond和_mutex
- 打个比喻线程就像一个旅人,Parker就像他随身携带的背包,条件变量_cond就好比背包的 , _counter就好比背包中的备用干粮(0位耗尽,1位充足)
- 调用park就是要看需不需要停下来歇息
- 如果备用干粮耗尽,那么钻进帐篷休息
- 如果备用干粮充足,那么不许停留,继续前进。
- 调用unpark,就好比令干粮充足
- 如果这时线程就在帐篷,就唤醒让他继续前进
- 如果这时线程还在运行,那么下次他调用park时,仅是消耗掉备用干粮,不许停留继续前进
- 因为背包空间有限,
多次调用unpark仅会补充一份备用干粮
先调用park再调用unpark的过程
- 先调用park的情况
- 当前线程调用 Unsafe.park()方法
- 检查_counter,是否为0,如果为0,获得
_mutex互斥锁(mutex对象有一个等待对了_cond)
- 线程进入_cond条件变量
阻塞
- 设置
_counter=0
(没干粮了)
-
调用unpark
- 调用
Unsafe.unpark(Thread_0)方法,设置_count为1
- 唤醒_cond条件变量中的Thread _0
- Thread_0
恢复运行
- 设置_counter为0
- 调用
先调用unpark再调用park的过程
- 调用
Unsafe.unparh(Thread_0)方法,
设置_count为1
- 当前
线程
调用Unsafe.park()
方法 - 检查
_counter
,本情况为1
,这时候线程无需阻塞,继续运行
- 设置_counter为0
线程状态转换(重点)
假设有线程Threa t
-
New<->Runnable
t.start()方法时
,new--->runnable
-
Runnable<->Waiting
- 线程用
synchronize(obj)
获取了对象所
后- 调用
obj.wait()
方法时,t线程进入了waitSet中,从Runnable->waiting
- 调用
obj.nofty()、obj.notifyAll()、t.interrupt()
时,唤醒的线程都到 entrySet阻塞队列成为BLOCKED
状态,在阻塞队列,和其他线程在进行竞争锁
- **竞争锁成功,**t线程从waiting->runnable
- 竞争锁失败,t线程从waiting->blocked
- 调用
- 线程用
-
runnable->waiting
- 当前线程调用
t.join()
方法时,当前线程从runnable->waiting
,注意是 当前线程在t线程对象在 waitSet上等待 - t线程运行结束,或调用了当前线程的interrupt()时,当前线程从
waiting->runnable
- 当前线程调用
-
RUNNABLE <–> WAITING
- 当前线程调用
LockSupport.park()
方法会让当前线程从RUNNABLE --> WAITING
- 调用
LockSupport.unpark(目标线程)
或调用了线程 的 interrupt() ,会让目标线程从WAITING --> RUNNABLE
Runnable 和 Timed-Waiting的相互转换
- 当前线程调用
-
RUNNABLE <–> TIMED_WAITING (带超时时间的wait)
- t 线程用
synchronized(obj)
获取了对象锁
后- 调用
obj.wait(long n)
方法时,t 线程从RUNNABLE --> TIMED_WAITING
- 线程等待时间超过了 n 毫秒,或调用
obj.notify() , obj.notifyAll() , t.interrupt()
时; 唤醒的线程都到entrySet阻塞队列成为BLOCKED
状态, 在阻塞队列,和其他线程再进行 竞争锁- 竞争锁成功,t 线程从 TIMED_WAITING --> RUNNABLE
- 竞争锁失败,t 线程从 TIMED_WAITING --> BLOCKED
- 调用
- t 线程用
-
RUNNABLE <–> TIMED_WAITING
- 当前线程调用
t.join(long n)
方法时,当前线程从RUNNABLE --> TIMED_WAITING
注意是当前线程在t 线程对象的waitSet等待 - 当前线程等待时间超过了 n 毫秒,或t 线程运行结束,或调用了当前线程的 interrupt() 时,当前线程从
TIMED_WAITING --> RUNNABLE
- 当前线程调用
-
RUNNABLE <–> TIMED_WAITING
- 当前线程调用
Thread.sleep(long n)
,当前线程从RUNNABLE --> TIMED_WAITING
- 当前线程等待时间超过了 n 毫秒或调用了线程的 interrupt() ,当前线程从
TIMED_WAITING --> RUNNABLE
- 当前线程调用
-
RUNNABLE <–> TIMED_WAITING
- 当前线程调用
LockSupport.parkNanos(long nanos) 或 LockSupport.parkUntil(long millis)
时,当前线程从RUNNABLE --> TIMED_WAITING
- 调用
LockSupport.unpark(目标线程) 或调用了线程 的 interrupt()
,或是等待超时,会让目标线程从TIMED_WAITING--> RUNNABLE
- 当前线程调用
-
RUNNABLE <–> BLOCKED
- t 线程用 synchronized(obj) 获取了对象锁时如果
竞争失败
,从RUNNABLE –> BLOCKED
, 持 obj 锁线程的同步代码块执行完毕,会唤醒该对象上所有 BLOCKED 的线程重新竞争,如果其中 t 线程竞争 成功,从 BLOCKED –> RUNNABLE ,其它失败的线程仍然 BLOCKED
- t 线程用 synchronized(obj) 获取了对象锁时如果
-
RUNNABLE <–> TERMINATED
- 当前线程所有代码运行完毕,进入 TERMINATED