step1
public class TestCorrectPostureStep1 {
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){
System.out.println("有烟没?"+hasCigarette);
if(!hasCigarette){
System.out.println("没烟,先歇会儿");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("有烟没?"+hasCigarette);
if(hasCigarette){
System.out.println("可以开始干活了");
}
}
}
},"小南").start();
for(int i=0;i<5;i++){
new Thread(()->{
synchronized (room){
System.out.println("可以干活了");
}
},"其他人").start();
}
Thread.sleep(1);
new Thread(()->{
hasCigarette=true;
System.out.println("烟送到了嗷!");
},"送烟的").start();
}
}
输出:
- 其它干活的线程,要被一直阻塞,效率太低
- 小南线程必须睡足2s后才能醒来,就算烟提前送到,也无法立刻醒来
- 加了synchronized(room)后,就好比小南在里边反锁了门睡觉,烟根本没法送进门,main没加synchronized就好像main线程是翻窗户进来的
- 解决方法,使用wait-notify机制
step2
对step1改进,将sleep改成wait-notify机制
public class TestCorrectPostureStep1 {
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){
System.out.println("有烟没?"+hasCigarette);
if(!hasCigarette){
System.out.println("没烟,先歇会儿");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("有烟没?"+hasCigarette);
if(hasCigarette){
System.out.println("可以开始干活了");
}
}
}
},"小南").start();
for(int i=0;i<5;i++){
new Thread(()->{
synchronized (room){
System.out.println("可以干活了");
}
},"其他人").start();
}
Thread.sleep(1);
new Thread(()->{
synchronized (room){
hasCigarette=true;
System.out.println("烟送到了嗷!");
room.notify();//唤醒一个线程
}
},"送烟的").start();
}
}
输出:
- 解决了其它干活的线程的阻塞问题
- 但如果有其它线程也在wait,那么notify可能不会将”小南”线程唤醒,因为notify是选择某一个线程唤醒。
step3
测试如果多个线程处于等待状态,notify是否会将特定线程唤醒。
public class TestCorrectPostureStep1 {
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){
System.out.println("有烟没?"+hasCigarette);
if(!hasCigarette){
System.out.println("没烟,先歇会儿");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("有烟没?"+hasCigarette);
if(hasCigarette){
System.out.println("可以开始干活了");
}else{
System.out.println("没干成活");
}
}
}
},"小南").start();
/*小女线程,等待外卖*/
new Thread(()->{
synchronized (room){
Thread thread = Thread.currentThread();
System.out.println("外卖送到没?"+hasTakeout);
if(!hasTakeout){
System.out.println("没外卖!先歇会儿");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("外卖送到没?"+hasTakeout);
if (hasTakeout){
System.out.println("可以干活了");
}
else{
System.out.println("没干成活");
}
}
},"小女").start();
Thread.sleep(1);
/*送外卖线程*/
new Thread(()->{
synchronized (room){
hasTakeout=true;
System.out.println("外卖到了嗷!");
room.notify();
}
},"送外卖的").start();
}
}
缺点:送外卖线程如果将小南线程唤醒,那么送烟线程没有执行,还是没干成活。属于虚假唤醒,错误地将小南线程唤醒,又没有满足送烟的条件。
一个改进的方式是:改用notifyAll方法,但是虽然将小女线程唤醒,还是会将小南线程虚假唤醒。
step4
step3的问题点在于”小南”线程当被虚假唤醒后,线程即会继续向下执行,解决办法很简单,对条件是否满足的判断从if改成while即可
while(!hasCigarette){
System.out.println("没烟,先歇会儿");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("有烟没?"+hasCigarette);
if(hasCigarette){
System.out.println("可以开始干活了");
}else{
System.out.println("没干成活");
}
}
当没满足条件时,则会继续wait,相当于你虚假唤醒我,那我继续睡就好了。
所以综上,wait-notify的正确写法为:
synchronized(lock){
while(条件不成立){
lock.wait();//都进入等待了,肯定是某条件没满足
}
//条件满足,继续执行其它代码
}
//另一个线程
synchronized(lock){
lock.notifyAll();//这里建议唤醒线程都用notifyAll
}