notify死锁代码示例
今天看到一个问题,说使用lock.notify方法有可能会导致死锁问题。
问题是由于notify是随机唤醒一个线程,有可能唤醒的线程是一个错误的线程(指不是期望唤醒的线程),线程仍然无法正常结束。
使用notify唤醒的是所有的线程,可以避免这个问题
代码示例如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| public class Main { static Object lock = new Object();
static Boolean flag1 = false; static Boolean flag2 = false;
public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { synchronized (lock) { while (!flag1) { System.out.println(Thread.currentThread().getName() + "等待"); try { lock.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } }
System.out.println(Thread.currentThread().getName() + "执行完成"); } }, "线程1");
Thread t2 = new Thread(() -> { synchronized (lock) { while (!flag2) { System.out.println(Thread.currentThread().getName() + "等待"); try { lock.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } }
System.out.println(Thread.currentThread().getName() + "执行完成"); } }, "线程2");
Thread t3 = new Thread(() -> { synchronized (lock) { System.out.println("修改flag2"); flag2 = true; lock.notify(); } });
t1.start(); t2.start(); Thread.sleep(1000); t3.start(); } }
|
上面的代码中,线程1和线程2都先进入到等待状态。
主线程睡眠1s后,线程3被开启,修改了flag2的值,并使用notify随机唤醒了一个线程。
这个时候发现,唤醒的线程并不是flag2对应的线程2,唤醒的线程是线程1。
线程1继续运行,发现还是不满足while的条件,继续走wait的逻辑。于是死锁问题便出现了。
代码运行的结果如下图所示:
解决方法
在线程3中,我们可以通过用notifyAll方法来代替notify方法。
notifyAll会将所有处于wait状态的线程唤醒。避免了唤醒了一个错误的线程的问题。
1 2 3 4 5 6 7 8
| Thread t3 = new Thread(() -> { synchronized (lock) { System.out.println("修改flag2"); flag2 = true; lock.notifyAll(); } });
|
调整后代码的运行结果如下:
分析运行结果,可以发现,线程1和线程2都被唤醒了。
线程2因为线程3更改了条件二,满足了while的条件,执行结束。
线程1条件仍不满足,所以还是等待状态。