问题1:wait状态的线程被唤醒后的执行流程
结论:调用wait阻塞的线程在重新获取锁之后继续执行后续代码块
验证场景:线程A循环打印1-10打印到5的时候 wait阻塞让出锁资源,线程B拿到锁之后执行对应的任务逻辑 最终唤醒所有的等待线程,让出锁资源
package MultiThread;
/**
* @author Ash_Camile
*/
public class WaitDemo {
public static Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock) {
for (int i = 0; i < 10; i++) {
if (i == 5) {
try {
lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("线程A继续执行。。。");
}
System.out.println("i = " + i);
}
}
}, "线程A").start();
try {
// 这个sleep是保证线程A比线程B先获取锁
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(() -> {
synchronized (lock) {
System.out.println("线程B获取锁");
System.out.println("线程B执行对应任务");
lock.notifyAll();
System.out.println("线程B释放锁");
}
}, "线程B").start();
}
}
运行结果:
问题2:那么,虚假唤醒的产生原因呢?
虚假唤醒的例子:多线程循环打印ABC时,用if判断当前state状态,导致未能正常唤醒应该唤醒的线程,导致打印错乱
循环打印ABC错乱代码如下:
package MultiThread;
/**
* @aythor YOLO
* @create 2022--10--19 11:26
*/
// 借助 :synchronized + state + notifyAll
public class PrintABC {
public static volatile int status = 0;
public static Object lock = new Object();
public static void main(String[] args) {
new BaseImpl(0, "A").start();
new BaseImpl(1, "B").start();
new BaseImpl(2, "C").start();
}
/**
* 优雅的写法!
*/
public static class BaseImpl extends Thread {
public static Object lock = new Object();
public static int state = 0;
// flag表示满足什么条件打印
public int flag;
// value表示打印什么
public String value;
public BaseImpl (int flag, String value){
this.flag = flag;
this.value = value;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (lock) {
if(state % 3 != flag) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 满足之后
System.out.println(value);
state++;
lock.notifyAll();
}
}
}
}
定位打印错乱的关键代码处:
if(state % 3 != flag) { // 注意这个if!
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
执行结果:
对上述问题场景描述:线程C打印完成,notifyAll,A和B被唤醒,但是我们希望的是A线程执行,B线程不执行
实际情况:A和B被同时唤醒,因此AB都有可能获取锁资源,我们希望的是 即使B获取了锁,但是仍然不能执行,仍然会释放锁,但是在if的判断条件下,被唤醒后,B线程他不会走if的继续判断了(即使他是不满足执行条件的),而是直接走下面B的执行;
而使用while的话 仍然会判断条件,不满足则直接释放锁,让真正应该执行的线程A执行;