儿时的游戏:(等待 与 唤醒)
有一群小朋友一起玩一个游戏,这个游戏可能大家都玩过,大家一起划拳,划拳输得最惨的那个小朋友去抓人(这个小朋友取名为 CPU),被抓的很多人取名为线程,有很多线程,如果其中一个小朋友(例如:Thread-3) 被木头了(wait();) 就站着不准动了(冻结状态),Thread-3小朋友站着不动(冻结状态) CPU小朋友就不会去抓Thread-3小朋友,因为Thread-3小朋友(释放CPU执行资格) 需要其他小朋友(例如:Thread-5) 去啪一下Thread-3小朋友(notify();), 此时Thread-3小朋友就可以跑了(被唤醒) 此时Thread-3小朋友具备被抓的资格(具备CPU执行资格),能否被抓得到 要看CPU小朋友的随机抓人法(CPU切换线程的随机性)
等待唤醒机制:
wait(); 等待/冻结 :可以将线程冻结,释放CPU执行资格,释放CPU执行权,并把此线程临时存储到线程池
notify(); 唤醒线程池里面 任意一个线程,没有顺序;
notifyAll(); 唤醒线程池里面,全部的线程;
使用等待唤醒注意事项:
1.使用来wait();冻结,就必须要被其他方notify();,否则一直wait()冻结,所以等待与唤醒是配合一起使用的;
2.wait(); notify(); notifyAll(); 等方法必须被synchronized(锁) {包裹起来},意思就是:wait(); notify(); notifyAll(); 必须要有同步锁🔒,否则毫无意义;
3.wait(); notify(); notifyAll(); 等方法必须持有同一把锁🔒,因为:lockA.wait(); 只能使用 lockA.notify(); / lockA.notifyAll(); , 它们是使用同一把锁🔒的;
等待:🔒锁.wait();
唤醒:🔒锁.notify();
唤醒:🔒锁.notifyAll();
代码分析 1:
❌❌❌❌❌ 错误的示范,public synchronized (锁🔒== this) void testMethod1(){} ,然后却是OBJ_LOCK.notify(); this锁🔒和 OBJ_LOCK🔒不一致,是错误的,没有共用一把锁
private final Object OBJ_LOCK = newObject();/*** public synchronized void testMethod1(){}
* synchronized的锁🔒是this / b = new TestWaitNotifyDemo(); b也是
* 所以可以:this.notify(); this.notifyAll(); this.wait();*/
public synchronized voidtestMethod1() {/*** 唤醒 wait(); 冻结的线程,如果没有就是空唤醒,Java是支持的*/OBJ_LOCK.notify();//注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
/*** 唤醒所有*/OBJ_LOCK.notifyAll();//注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
/*** 当前自己线程 冻结,释放CPU执行资格,释放CPU执行权,CPU就会去执行其他线程了*/
try{
OBJ_LOCK.wait();//注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
} catch(InterruptedException e) {
e.printStackTrace();
}
}
✅✅✅✅✅,正确的方式:public synchronized(🔒锁==this) void testMethod1, this.notify(); this.notifyAll(); this.wait();
/*** public synchronized void testMethod1(){}
* synchronized的锁🔒是this / b = new TestWaitNotifyDemo(); b也是
* 所以可以:this.notify(); this.notifyAll(); this.wait();*/
public synchronized voidtestMethod1() {/*** 唤醒 wait(); 冻结的线程,如果没有就是空唤醒,Java是支持的*/
this.notify(); //注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
/*** 唤醒所有*/
this.notifyAll(); //注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
/*** 当前自己线程 冻结,释放CPU执行资格,释放CPU执行权,CPU就会去执行其他线程了*/
try{this.wait(); //注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
} catch(InterruptedException e) {
e.printStackTrace();
}
}
代码分析 1 伪代码:
public synchronized(🔒锁是this) voidtestMethod1() {
锁🔒this.notify(); //注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
锁🔒this.notifyAll(); //注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
锁🔒this.wait(); //注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
}
代码分析 2:
错误的示范❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌ synchronized (锁🔒== OBJ_LOCK) ,然后却是this.notify(); this锁🔒和 OBJ_LOCK🔒不一致,是错误的
private final Object OBJ_LOCK = newObject();public void testMethod2() throwsInterruptedException {/*** synchronized (OBJ_LOCK) {}
* synchronized的锁🔒是 OBJ_LOCK
* 所以可以:OBJ_LOCK.notify(); OBJ_LOCK.notifyAll(); OBJ_LOCK.wait();*/
synchronized(OBJ_LOCK) {/*** 唤醒 wait(); 冻结的线程,如果没有就是空唤醒,Java是支持的*/
this.notify(); //注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
/*** 唤醒所有*/
this.notifyAll(); //注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
/*** 当前自己线程 冻结,释放CPU执行资格,释放CPU执行权,CPU就会去执行其他线程了*/
this.wait(); //注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
}
}
正确的示范✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅synchronized (锁🔒== OBJ_LOCK) ,然后是OBJ_LOCK.notify(); 使用了同一把OBJ_LOCK锁🔒,是正确的✅
private final Object OBJ_LOCK = newObject();public void testMethod2() throwsInterruptedException {/*** synchronized (OBJ_LOCK) {}
* synchronized的锁🔒是 OBJ_LOCK
* 所以可以:OBJ_LOCK.notify(); OBJ_LOCK.notifyAll(); OBJ_LOCK.wait();*/
synchronized(OBJ_LOCK) {/*** 唤醒 wait(); 冻结的线程,如果没有就是空唤醒,Java是支持的*/OBJ_LOCK.notify();//注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
/*** 唤醒所有*/OBJ_LOCK.notifyAll();//注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
/*** 当前自己线程 冻结,释放CPU执行资格,释放CPU执行权,CPU就会去执行其他线程了*/OBJ_LOCK.wait();//注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
}
}
代码分析 2 伪代码:
private final Object OBJ_LOCK = newObject();public void testMethod2() throwsInterruptedException {synchronized(OBJ_LOCK🔒锁) {/*** 唤醒 wait(); 冻结的线程,如果没有就是空唤醒,Java是支持的*/OBJ_LOCK🔒锁.notify();//注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
/*** 唤醒所有*/OBJ_LOCK🔒锁.notifyAll();//注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
/*** 当前自己线程 冻结,释放CPU执行资格,释放CPU执行权,CPU就会去执行其他线程了*/OBJ_LOCK🔒锁.wait();//注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着
}
}