wait 和 notify
512字约2分钟
2024-08-08
在 Java
程序中,synchronized
解决了多线程竞争的问题。例如,对于一个任务管理器,多个线程同时往队列中添加和获取任务,可以用 synchronized
加锁,但是并没有解决多线程协调的问题
class TaskQueue {
Queue<String> queue = new LinkedList<>();
public synchronized void addTask(String s) {
this.queue.add(s);
}
public synchronized String getTask() {
while (queue.isEmpty()) {
}
return queue.remove();
}
}
上述代码看上去没有问题,但实际上 while()
循环永远不会退出。因为线程在执行 while()
循环时,已经在 getTask()
入口获取了 this
锁,其他线程根本无法调用 addTask()
,因为 addTask()
执行条件也是获取 this
锁
如果深入思考一下,我们想要的执行效果是:
线程
1
可以调用addTask()
不断往队列中添加任务线程
2
可以调用getTask()
从队列中获取任务。如果队列为空,则getTask()
应该等待,直到队列中至少有一个任务时再返回
因此,多线程协调运行的原则就是:当条件不满足时,线程进入等待状态;当条件满足时,线程被唤醒,继续执行任务
改造上述TaskQueue
,
getTask()
方法,在条件不满足时,线程进入等待状态addTask()
方法,添加之后唤醒其他线程
class TaskQueue {
Queue<String> queue = new LinkedList<>();
public synchronized void addTask(String s) {
this.queue.add(s);
this.notifyAll();
}
public synchronized String getTask() throws InterruptedException {
while (queue.isEmpty()) {
this.wait();
}
return queue.remove();
}
}
addTask()
方法内部调用了 this.notifyAll()
而不是 this.notify()
,使用 notifyAll()
将唤醒所有当前正在 this
锁等待的线程,而 notify()
只会唤醒其中一个(具体哪个依赖操作系统,有一定的随机性)。这是因为可能有多个线程正在 getTask()
方法内部的 wait()
中等待,使用 notifyAll()
将一次性全部唤醒。通常来说,notifyAll()
更安全。有些时候,如果我们的代码逻辑考虑不周,用 notify()
会导致只唤醒了一个线程,而其他线程可能永远等待下去醒不过来了