在生产者和消费者问题中,使用wait()方法可能造成的虚假唤醒和解决方法
问题代码:
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data{//判断等待
private int number=0;
//+1
public synchronized void increment() throws InterruptedException {
if (number!=0){
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"->"+number);
//通知其他线程,我+1完毕了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
if (number==0){
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"->"+number);
//通知其他线程,我-1完毕了
this.notifyAll();
}
}
错误运行结果
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
C->1
A->2
C->3
B->2
B->1
B->0
C->1
A->2
C->3
B->2
D->1
D->0
C->1
A->2
C->3
D->2
D->1
C->2
A->3
C->4
D->3
D->2
D->1
D->0
C->1
D->0
C->1
D->0
出现了不是0或1的数据。
原因:
判断number!=0和number==0时,使用了if进行判断,造成了虚假唤醒。
解决方法:
阅读JDK1.8的帮助文档
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).
The current thread must own this object’s monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object’s monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.
As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:
synchronized (obj) {
while ()
obj.wait();
… // Perform action appropriate to condition
}
翻译内容:
所以不能使用if进行判断,应该使用while循环进行判断
正确代码:
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data{//判断等待
private int number=0;
//+1
public synchronized void increment() throws InterruptedException {
while (number!=0){
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"->"+number);
//通知其他线程,我+1完毕了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
while (number==0){
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"->"+number);
//通知其他线程,我-1完毕了
this.notifyAll();
}
}
正确运行结果:
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
C->1
B->0
A->1
B->0
C->1
B->0
A->1
B->0
C->1
B->0
A->1
D->0
C->1
B->0
A->1
D->0
C->1
D->0
A->1
D->0
C->1
D->0
A->1
D->0
C->1
D->0
C->1
D->0
C->1
D->0
C->1
D->0
问题解决!