等待与通知: wait/notify
多线程编程中,如果某线程执行的条件没有满足,可以先将这个线程暂停,等到其所需要的条件满足了再将其唤醒。伪代码如下:
1
2
3
4
5
6
7
| atomic{
while(保护条件不成立){
暂停当前线程;
}
//执行目标动作
doAction();
}
|
判断+执行 应该具有原子性。条件未满足而暂停被称为等待。一个线程更新了系统的状态,使得其他线程所需的条件得以满足的时候唤醒哪些被暂停的线程的过程叫做通知(Notify)。
在Java中,Object中有两个方法wait() notify()分别表示等待和通知,作用对象为当前线程。
看一个例子
Main.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| public class Main {
public static void main(String[] args) {
Gainer gainer = new Gainer();
for (int i = 0; i < 10; i++) {
new Thread(()->{
try {
gainer.gainSource();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
Scanner scanner = new Scanner(System.in);
int a = scanner.nextInt();
gainer.addSource(a);
}
}
|
Gainner.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| public class Gainer {
public static int sources = 1;
public synchronized boolean gainSource() throws InterruptedException {
if (sources <= 0){
System.out.println(Thread.currentThread().getName() + "没有抢到资源");
wait();
}
sources--;
System.out.println(Thread.currentThread().getName() + "抢到了资源");
return true;
}
public synchronized void addSource(int sourceCount){
sources += sourceCount;
this.notifyAll();
}
}
|
这个代码测试的是多线程的资源抢占,初始资源数为1,然后通过输入流加3,运行结果如下:

第一部分的结果的符合预期的,然后我通过输入3使资源数+3,随后唤醒所有等待线程竞争,之后的结果就不对了,显示所有的线程都抢到了资源。
问题在哪呢,一个很细节的地方,在条件判断那里我写了if (sources <= 0){,线程第一次进来,如果没有可用资源,遇到wait方法,等待,如果未来的某个时间点该等待线程被唤醒了,那会继续从该处就是wait方法执行下去,那么如果用if,只会判断一次,所以在唤醒之后的那次就没有条件判断了,所以每个等待线程都抢到了资源。
需要将if改为while,并将wait放在while中的最后,这样,每当等待线程被唤醒的时候,直接回到条件判断。
另外注意main方法中,我在循环体外面定义了Gainer,刚开始的时侯尝试在线程体里面new,但这样发现同步方法没有用,因为非静态同步方法的锁对象是当前对象,所以如果在线程体里面new出来Gainer,那么就有10个对象10个锁,每个线程各占用一个锁,就不对了,一定要保证全局只有一把锁,另一种是可以使用静态变量作为锁。