【发布时间】:2017-02-09 14:50:09
【问题描述】:
我正在使用wait() 和notify() 编写示例程序,但是当调用notify() 时,会唤醒多个线程而不是一个。
代码是:
public class MyQueue<T> {
Object[] entryArr;
private volatile int addIndex;
private volatile int pending = -1;
private final Object lock = new Object();
private volatile long notifiedThreadId;
private int capacity;
public MyQueue(int capacity) {
entryArr = new Object[capacity];
this.capacity = capacity;
}
public void add(T t) {
synchronized (lock) {
if (pending >= 0) {
try {
pending++;
lock.wait();
System.out.println(notifiedThreadId + ":" + Thread.currentThread().getId());
} catch (InterruptedException e) {
e.printStackTrace();
}
} else if (pending == -1) {
pending++;
}
}
if (addIndex == capacity) { // its ok to replace existing value
addIndex = 0;
}
try {
entryArr[addIndex] = t;
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("ARRAYException:" + Thread.currentThread().getId() + ":" + pending + ":" + addIndex);
e.printStackTrace();
}
addIndex++;
synchronized (lock) {
if (pending > 0) {
pending--;
notifiedThreadId = Thread.currentThread().getId();
lock.notify();
} else if (pending == 0) {
pending--;
}
}
}
}
public class TestMyQueue {
public static void main(String args[]) {
final MyQueue<String> queue = new MyQueue<>(2);
for (int i = 0; i < 200; i++) {
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
queue.add(Thread.currentThread().getName() + ":" + i);
}
}
};
Thread t = new Thread(r);
t.start();
}
}
}
一段时间后,我看到两个线程被单线程唤醒。输出如下:
91:114
114:124
124:198
198:106
106:202
202:121
121:40
40:42
42:83
83:81
81:17
17:189
189:73
73:66
66:95
95:199
199:68
68:201
201:70
70:110
110:204
204:171
171:87
87:64
64:205
205:115
这里我看到115线程通知了两个线程,84线程通知了两个线程;因此,我们看到了ArrayIndexOutOfBoundsException。
115:84
115:111
84:203
84:200
ARRAYException:200:199:3
ARRAYException:203:199:3
程序有什么问题?
【问题讨论】:
-
您似乎错过了
synchronized的实际用途。这是为了保护对共享资源的访问,而不是在访问完全不受保护的共享资源时执行等待和通知。此外,你应该仔细阅读Object.wait()的文档,尤其是“…spurious wakeups are possible, and this method should always be used in a loop”部分。 -
感谢您的快速回复。我知道我们可以使用并发锁。但我的任务是使用wait() 和notify() 进行锁定。所以在任何时候,只有一个线程应该执行同步块之间的代码。
-
我在评论中的什么地方提到了“并发锁”?
synchronized块必须跨越整个操作,包括对共享数据结构的每次访问,而不仅仅是执行wait或notify的部分。您正在访问synchronized块之外的entryArr和addIndex并将addIndex声明为volatile没有帮助,因为它不会使更新成为原子。
标签: java multithreading wait notify