notify()表现得像notifyAll()

Ali*_*Ali 11 java multithreading

我有一个计算器线程计算1到50之间的数字之和,以及一个计算器线程准备好后显示结果的多个Reader线程.我可以选择调用notify()和notifyAll()来通知Reader线程计算结果已准备好显示.在Calculator类的LINE B中,如果我调用notifyAll()方法,结果将按预期打印4次.但是当我用仅通知()替换LINE B时,我仍然看到打印4次的结果似乎不正确.据我所知,notify()只会唤醒其中一个正在等待的线程,而不是所有线程.当我调用notify时,为什么所有线程都会唤醒并打印结果?

public class ThreadWaitNotify {

    public static void main(String[] args) {
        Calculator c = new Calculator();
        Reader r = new Reader(c);
        Reader r2 = new Reader(c);
        Reader r3 = new Reader(c);
        Reader r4 = new Reader(c);

        r.start();
        r2.start();
        r3.start();
        r4.start();
        try {
            Thread.sleep(500L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        c.start();
    }

}
Run Code Online (Sandbox Code Playgroud)

读者课程:

class Reader extends Thread {

    Calculator c;

    public Reader(Calculator c) {
        this.c = c;
    }

    @Override
    public void run() {
        synchronized (c) {
            try {
                System.out.println(Thread.currentThread().getName() + " Waiting for calculations: ");
                c.wait();    // LINE A
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " Total: " + c.getSum());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

计算器类:

class Calculator extends Thread {

    private int sum = 0;

    @Override
    public void run() {
        synchronized (this) {
            for (int i = 1; i <= 50; i++) {
                sum += i;
            }
            notify();  // LINE B
        }
    }

    public int getSum() {
        return sum;
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

Thread-1 Waiting for calculations: 
Thread-4 Waiting for calculations: 
Thread-3 Waiting for calculations: 
Thread-2 Waiting for calculations: 
Thread-1 Total: 1275
Thread-2 Total: 1275
Thread-3 Total: 1275
Thread-4 Total: 1275
Run Code Online (Sandbox Code Playgroud)

======================

更新:使用对象作为监视器/锁而不是Thread实例会产生正确的行为.

更新了ThreadWaitNotify类:

public class ThreadWaitNotify {

    public static void main(String[] args) {
        Object monitor = new Object();
        Calculator c = new Calculator(monitor);
        Reader r = new Reader(c, monitor);
        Reader r2 = new Reader(c, monitor);
        Reader r3 = new Reader(c, monitor);
        Reader r4 = new Reader(c, monitor);

        r.start();
        r2.start();
        r3.start();
        r4.start();
        try {
            Thread.sleep(500L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        c.start();
    }

}
Run Code Online (Sandbox Code Playgroud)

更新的阅读器类:

class Reader extends Thread {

    Calculator c;
    Object monitor;

    public Reader(Calculator c, Object monitor) {
        this.c = c;
        this.monitor = monitor;
    }

    @Override
    public void run() {
        synchronized (monitor) {
            try {
                System.out.println(Thread.currentThread().getName() + " Waiting for calculations: ");
                monitor.wait();   // LINE A
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " Total: " + c.getSum());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

更新的计算器类:

class Calculator extends Thread {

    private int sum = 0;
    Object monitor;

    public Calculator(Object monitor) {
        this.monitor = monitor;
    }

    @Override
    public void run() {
        synchronized (monitor) {
            for (int i = 1; i <= 50; i++) {
                sum += i;
            }
            monitor.notify();       // LINE B
        }
    }

    public int getSum() {
        return sum;
    }
}
Run Code Online (Sandbox Code Playgroud)

Izr*_*ruo 11

这并不会notify()唤醒所有读者Threads,而是终结Calculator's Thread's.

直到现在我也不知道这种行为,但看起来终止Thread所有Threads等待它的人都会醒来.Thread.sleep()在结束时再扔另一个,Calculator.run()你会看到.

更新

我刚刚意识到阅读John的答案,这是一个至关重要的区别.

误解存在于"等待它"这一短语中.A Thread确实会通知所有服务员,但它与Thread概念无关.

事实上,这是一种特殊的行为,特别是a Thread,只要它达到寿命的终点,就会通知所有等待Thread 对象本身的服务员.正如John已经指出的那样,这将发生在'after'之后的某个时刻Thread.exit(),因此在JVM中,因此与对象释放无关.

结论

虽然这很可能是所描述错误的原因,但绝不应该依赖JVM内部发生的任何事情.事实证明,这种行为是一个明确的指标.

  • @bashnesnos注意线程*不被*解除分配. (3认同)

Joh*_*int 9

我原来的答案是错的.我刚刚浏览了本机代码,了解发生了什么.

当一个线程结束时,它实际上会通知退出线程监视器上的所有等待线程.同样,这是在本机级别,并且可以改变

thread.cpp - >

void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
      ..... other code ....
      // Notify waiters on thread object. This has to be done after exit() is called
      // on the thread (if the thread is the last thread in a daemon ThreadGroup the
      // group should have the destroyed bit set before waiters are notified).
      ensure_join(this);
      assert(!this->has_pending_exception(), "ensure_join should have cleared");
     .... other code ....
Run Code Online (Sandbox Code Playgroud)

这是来自JDK 7的源代码,但是我无法想象这个功能会有很大的不同.