我正在使用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
该计划有什么问题?
答案 0 :(得分:1)
该计划有什么问题?
您的代码存在一些可能导致此行为的问题。首先,正如@Holder所评论的那样,有很多代码段可以由多个线程同时运行,应该使用synchronized
块进行保护。
例如:
if (addIndex == capacity) {
addIndex = 0;
}
如果有多个线程运行,那么多个线程可能会看到addIndex == capacity
,而多个线程会覆盖第0个索引。另一个例子是:
addIndex++;
如果2个线程同时尝试执行此语句,则这是一个经典的竞争条件。如果事先addIndex
为0,则在2个线程执行此语句后,addIndex
的值可能为1或2,具体取决于竞争条件。
任何可由多个线程同时执行的语句都必须在synchronized
块内正确锁定或以其他方式受到保护。即使您有volatile
个字段,仍然可能存在竞争条件,因为正在执行多个操作。
另外,一个经典的错误是在检查数组上的流量过低或过低时使用if
语句。它们应该是while
陈述,以确保您没有类消费者生产者竞争条件。请参阅my docs here或查看相关的SO问题:Why does java.util.concurrent.ArrayBlockingQueue use 'while' loops instead of 'if' around calls to await()?