这是我的代码和输出在我运行代码的任何时候都是不同的。有时会通知所有三位读者并输出:
等待计算...
等待计算...
等待计算...
成品
总计是:4950Thread-1
总计是:4950Thread-2
总计是:4950Thread-0
有时只会通知两个或一个读者。 有什么问题?
class Reader extends Thread {
Calculator c;
public Reader(Calculator calc) {
c = calc;
}
public void run() {
synchronized (c) {
try {
System.out.println("Waiting for calculation...");
c.wait();
} catch (InterruptedException e) {
}
System.out.println("Total is: " + c.total +Thread.currentThread().getName());
}
}
public static void main(String[] args) {
Calculator calculator = new Calculator();
new Reader(calculator).start();
new Reader(calculator).start();
new Reader(calculator).start();
new Thread(calculator).start();
}
}
class Calculator implements Runnable {
int total;
public void run() {
synchronized (this) {
for (int i = 0; i < 100; i++) {
total += i;
}
System.out.println("Finished");
notifyAll();
}
}
}
根据元文章,这个问题被认为是重复的,但两个被重复的“重复”根本不适用。 How to use wait and notify in Java?提醒用户,如果您真的想等待同一个对象,则必须在该对象上进行同步。但是这个解决方案已经在做了。 Java: notify() vs. notifyAll() all over again提醒用户notify
和notifyAll
之间存在差异,甚至进一步问题。
答案 0 :(得分:1)
我能够重现这个问题 - 基本上,如果你的计算机太快或计划不好,计算器有可能在读者有机会同步和等待之前完成。
c:\ files \ j&gt; java Reader
等待计算...
等待计算...
完成了 等待计算...
总计:4950Thread-2
总计是:4950Thread-0
为了防止这种情况发生,您应该在执行计算之前验证所有读者是否已准备就绪。
c:\ files \ j&gt; java Reader 等待计算...... 等待读者...目前1 等待计算...... 等待计算...... 成品 总计是:4950Thread-1 总计是:4950Thread-2 总计是:4950Thread-0
这是我的代码
import java.util.concurrent.atomic.AtomicInteger; // corsiKa added import
class Reader extends Thread {
static class Calculator implements Runnable {
int total;
AtomicInteger readers = new AtomicInteger(0); // corsiKa added atomicinteger
public void run() {
// corsiKa added while
while(readers.get() < 3) {
System.out.println("Waiting for readers... currently " + readers.get());
try { Thread.sleep(100); } catch(InterruptedException e) { }
}
synchronized (this) {
for (int i = 0; i < 100; i++) {
total += i;
}
System.out.println("Finished");
notifyAll();
}
}
}
Calculator c;
public Reader(Calculator calc) {
c = calc;
}
public void run() {
synchronized (c) {
try {
c.readers.incrementAndGet(); // corsiKa added increment
System.out.println("Waiting for calculation...");
c.wait();
} catch (InterruptedException e) {
}
System.out.println("Total is: " + c.total +Thread.currentThread().getName());
}
}
public static void main(String[] args) {
Calculator calculator = new Calculator();
new Reader(calculator).start();
new Reader(calculator).start();
new Reader(calculator).start();
new Thread(calculator).start();
}
}
答案 1 :(得分:0)
代码的问题在于未定义线程的开始顺序。直觉上预期的行为是读者首先开始,然后是计算器。这正是您在上面显示的日志中发生的情况。
但也有其他交错可能。调用Thread.start不会对起始顺序做出任何保证。假设两个读者首先开始,然后是计算器,然后是最后一个读者。在这种情况下,计算器可以在第三个读取器之前进入临界区。计算器中的notifyAll调用然后在第三个读取器执行其等待调用之前发生。因此,第三个读者被永远等待,因为锁定对象上不会发生notify或notifyAll的其他调用。
问题的一个可能解决方案是使用CountDownLatch,让你让计算器等到所有三个读者都准备就绪。
答案 2 :(得分:0)
让您的主要方法检查其他读者的状态。
public static void main(String[] args) {
Calculator calculator = new Calculator();
Reader read1 = new Reader(calculator);
read1.start();
Reader read2 = new Reader(calculator);
read2.start();
Reader read3 = new Reader(calculator);
read3.start();
while(read1.getState() != Thread.State.WAITING &&
read2.getState() != Thread.State.WAITING &&
read3.getState() != Thread.State.WAITING){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
new Thread(calculator).start();
}
这将确保您绝对让所有线程都等待,并且每次都会产生相同的结果。