前言和结论
今天重新把昨晚的线程同步面试题做一遍时,发现实际情况运行下来时,线程一直不同步。后来经过不断测试,发现自己的一个误区。之前一直以为,线程如果被唤醒后再次执行时,会从头开始运行这个线程,也就是重新运行Runnable中的run()方法;而实际情况是,被唤醒并且被执行的线程是从上次阻塞的位置从下开始运行,也就是从wait()方法后开始执行。同时又对另一个问题进行了验证,为什么要用while判断循环条件,我用if不行么,前几天总是在想,于是决定动手验证下,实践才是检验真理的唯一标准呀,这里先把我验证的结论贴上:用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,wait之后的代码执行完后,会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。
代码实例1:
这里使用ReentrantLock进行验证,代码比较简单可直接复制到编译器里运行调试即可,这个例子先用来验证被唤醒后从哪里开始执行:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class AwaitSignal {
private static ReentrantLock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
private static volatile boolean flag = false;
public static void main(String[] args) {
Thread waiter = new Thread(new waiter());
waiter.start();
Thread signaler = new Thread(new signaler());
signaler.start();
}
static class waiter implements Runnable {
@Override
public void run() {
lock.lock();
System.out.println("我获取锁以后1");
try {
System.out.println("我进入try块里2");
while (!flag) {
System.out.println(Thread.currentThread().getName() + "当前条件不满足等待");
try {
System.out.println("我准备睡觉觉啦3。。。-----waiter");
condition.await();
System.out.println("我被叫醒后4。。。-------waiter");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "接收到通知条件满足");
} finally {
lock.unlock();
}
}
}
static class signaler implements Runnable {
@Override
public void run() {
lock.lock();
try {
flag = true;
System.out.println("我要叫醒你啦,你个小兔崽子睡睡睡------------signaler");
condition.signalAll();
} finally {
lock.unlock();
}
}
}
}
代码执行结果和分析:
简单分析:main方法中首先创建了waiter线程并调用,然后创建signaler线程调用,所以基本上都是waiter线程先执行,第一步我获取锁以后1,第二步,我进入try块里2,第三步while(!flag)为true,进入代码块,条件不满足,准备睡觉觉3,调用await进入休息室,此时signaler线程获取锁,并将flag设置为true,打印我要叫醒你啦小兔崽子,waiter醒来之后,首先接着await后的代码执行,打印我被叫醒后4,接着会再次进入while循环判断,此时已经不满足条件,会跳出循环,接下来打印"接收到通知条件满足",这里的代码逻辑一定要搞清楚,有助于理解。
代码实例2:
接下来我们进行if和while的验证,其实只要明白了上面的执行步骤,这里也清楚地,只是验证下结果:我们将signaler中的flag改为false,分别验证while和if的情况:
flag为false并用if验证的结果:
这里flag为false,被唤醒后条件并不满足,但是仍然继续向下执行代码逻辑,会存在虚假唤醒问题,就像你去大宝剑按摩按摩脚底板,这时没技师,你先在休息区等待,过了一会有人通知你可以啦,但是你起身过去,等着按摩脚底板时,发现仍然没有闲暇的技师,于是你愤怒离开,发誓再也不来,发奋学习,从此损失了一个顾客,为国家少了税收,所以不能用if
flag为false并用while验证的结果:
我们这里用while则很好的避免了上述问题,通知你有技师,然后醒来后,你又确认了下,发现还没有,于是你并没有起身过去,而是继续休息。
2个结果对比,应该很清楚了吧大家。