简体   繁体   English

notifyAll()不会通知所有线程

[英]notifyAll() does not notify all threads

This is my code and output are different anytime that I run the code. 这是我的代码,运行我的代码时输出都是不同的。 Sometimes all three readers will be notified and output is: 有时会通知所有三个读者,并且输出是:

Waiting for calculation... 等待计算...

Waiting for calculation... 等待计算...

Waiting for calculation... 等待计算...

Finished 已完成

Total is: 4950Thread-1 总数是:4950Thread-1

Total is: 4950Thread-2 总数是:4950Thread-2

Total is: 4950Thread-0 总数是:4950线程-0

and sometimes just two or one reader will be notified. 有时只会通知两到一个读者。 what is the problem? 问题是什么?

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();
        }
    }
}

As per the meta post, this question is claimed to be a duplicate, but both "duplicates" that have been dupe-hammered simply do not apply. 根据元发布,这个问题被认为是重复的,但是被欺骗的两个“重复”根本不适用。 How to use wait and notify in Java? 如何在Java中使用等待和通知? reminds users that if you truly want to wait on the same object, you have to synchronize on that object. 提醒用户,如果您确实要等待同一对象,则必须在该对象上进行同步。 But this solution is already doing this. 但是此解决方案已在执行此操作。 Java: notify() vs. notifyAll() all over again reminds users the difference between notify and notifyAll which is even further from the problem. Java的:通知()与notifyAll的()各地再次提醒用户之间的差异notifynotifyAll进一步从问题。

I was able to reproduce the problem - basically, if you have too fast of a computer, or a bad scheduling, it's possible for the calculator to finish before the reader has an opportunity to synchronize and wait. 我能够重现该问题-基本上,如果您的计算机速度太快或调度不正确,计算器有可能在读者有机会进行同步和等待之前完成操作。

c:\\files\\j>java Reader c:\\ files \\ j> java Reader
Waiting for calculation... 等待计算...
Waiting for calculation... 等待计算...
Finished 已完成
Waiting for calculation... 等待计算...
Total is: 4950Thread-2 总数是:4950Thread-2
Total is: 4950Thread-0 总数是:4950线程-0

To prevent this, you should verify that all your readers are ready before performing the calculation. 为避免这种情况,您应该在执行计算之前验证所有阅读器均已准备就绪。

c:\\files\\j>java Reader Waiting for calculation... Waiting for readers... currently 1 Waiting for calculation... Waiting for calculation... Finished Total is: 4950Thread-1 Total is: 4950Thread-2 Total is: 4950Thread-0 c:\\ files \\ j> java Reader正在等待计算...正在等待读者...当前1正在等待计算...正在等待计算...已完成总计为:4950Thread-1总计为:4950Thread-2总计为:4950线程-0

Here is my code 这是我的代码

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();
    }
}

The problem with your code is that the thread's start order is not defined. 您的代码的问题是未定义线程的启动顺序。 The intuitively expected behavior is that the readers start first, then the calculator. 直观上的预期行为是读者首先开始,然后是计算器。 This is exactly what happened in the log you show above. 这正是您在上面显示的日志中发生的情况。

But there are other interleavings possible as well. 但是也可能存在其他交错。 Calling Thread.start does not make any guarantees about the order of the starting. 调用Thread.start不能保证启动的顺序。 Assume two readers start first, then the calculator, then the last reader. 假设首先有两个阅读器,然后是计算器,然后是最后一个阅读器。 In this case the calculator can enter the critical section before the third reader. 在这种情况下,计算器可以在第三个阅读器之前进入关键部分。 The call of notifyAll from the calculator then happens before the third reader executes its wait call. 然后,计算器发出的notifyAll调用将在第三个阅读器执行其等待调用之前进行。 Hence the third reader is damned to wait forever as not other call of notify or notifyAll will occur on the lock object. 因此,第三个阅读器被迫永远等待,因为不会在锁对象上发生其他的notifynotifyAll调用。

One possible solution to your problem is to use a CountDownLatch which allows you to let the calculator wait until all three readers are ready. 解决此问题的一种可能方法是使用CountDownLatch ,它使您可以让计算器等待,直到所有三个读取器都准备就绪。

Make your main method check the other Reader's states. 使您的主要方法检查其他阅读器的状态。

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();
}

This will insure that you absolutely have all the threads waiting, and yields the same result every time. 这将确保您绝对有所有线程在等待,并且每次都产生相同的结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM