代码之家  ›  专栏  ›  技术社区  ›  Sergey Mikhanov

Java:NoTIFY()vs. NoTyjayAL()

  •  336
  • Sergey Mikhanov  · 技术社区  · 17 年前

    如果一个谷歌搜索“之间的区别 notify() notifyAll() “然后会出现很多解释(将javadoc段落分开)。这一切归结为被唤醒的等待线程数:一个在 通知() 以及所有 通知() .

    但是(如果我正确理解这些方法之间的区别),总是只选择一个线程进行进一步的监视采集;第一种情况是由VM选择的线程,第二种情况是由系统线程调度程序选择的线程。程序员不知道这两种方法(一般情况下)的确切选择过程。

    什么是 有用的 两者之间的差异 notify() notifyAll() 那么呢?我错过什么了吗?

    26 回复  |  直到 7 年前
        1
  •  237
  •   Solomon Slow    7 年前

    但是(如果我正确理解这些方法之间的区别),总是只选择一个线程进行进一步的监视采集。

    这是不正确的。 o.notifyAll() 唤醒 全部的 阻塞的线程数 o.wait() 电话。线程只能从返回 O. WAIT() 一个接一个,但他们各自 轮到他们了。


    简单地说,这取决于线程等待通知的原因。您想告诉其中一个等待线程发生了什么,还是想同时告诉所有线程?

    在某些情况下,一旦等待结束,所有等待线程都可以采取有用的操作。例如,一组等待某个任务完成的线程;一旦任务完成,所有等待的线程都可以继续其业务。在这种情况下,你会使用 通知() 同时唤醒所有等待的线程。

    另一种情况,例如互斥锁,只有一个等待线程在收到通知后可以做一些有用的事情(在本例中,获取锁)。在这种情况下,你宁愿使用 通知() . 正确实施,你 能够 使用 通知() 在这种情况下也一样,但是您将不必要地唤醒那些无论如何都不能做任何事情的线程。


    在许多情况下,等待条件的代码将被编写为一个循环:

    synchronized(o) {
        while (! IsConditionTrue()) {
            o.wait();
        }
        DoSomethingThatOnlyMakesSenseWhenConditionIsTrue_and_MaybeMakeConditionFalseAgain();
    }
    

    这样,如果 O.通知() 调用唤醒多个等待线程,第一个线程从 O. WAIT() 使使使条件保持假状态,然后唤醒的其他线程将返回等待状态。

        2
  •  306
  •   xagyg    14 年前

    显然, notify 唤醒(任何)等待集中的一个线程, notifyAll 唤醒等待集中的所有线程。下面的讨论应该消除任何疑问。 通知所有 应该经常使用。如果您不确定要使用哪个,请使用 通知所有 。请参阅下面的解释。

    仔细阅读并理解。如果您有任何问题,请给我发一封电子邮件。

    看看生产者/消费者(假设是一个具有两种方法的ProducerConsumer类)。它坏了(因为它使用 通知 )-是的,它可能会起作用——即使大多数时候,但它也可能导致死锁——我们将看到原因:

    public synchronized void put(Object o) {
        while (buf.size()==MAX_SIZE) {
            wait(); // called if the buffer is full (try/catch removed for brevity)
        }
        buf.add(o);
        notify(); // called in case there are any getters or putters waiting
    }
    
    public synchronized Object get() {
        // Y: this is where C2 tries to acquire the lock (i.e. at the beginning of the method)
        while (buf.size()==0) {
            wait(); // called if the buffer is empty (try/catch removed for brevity)
            // X: this is where C1 tries to re-acquire the lock (see below)
        }
        Object o = buf.remove(0);
        notify(); // called if there are any getters or putters waiting
        return o;
    }
    

    首先,

    为什么我们需要一个关于等待的while循环?

    我们需要一个 while 循环以防出现这种情况:

    使用者1(c1)进入同步块,缓冲区为空,因此c1被放入等待集(通过 wait 打电话)使用者2(c2)将要输入同步方法(在上面的点y处),但生产者p1将对象放在缓冲区中,然后调用 通知 . 唯一等待的线程是c1,所以它被唤醒,现在试图在点x(上面)重新获取对象锁。

    现在C1和C2正试图获取同步锁。其中一个(非确定性地)被选择并进入方法,另一个被阻塞(不是等待而是阻塞,试图获取方法的锁)。假设C2先拿到锁。c1仍在阻塞(试图在x处获取锁)。C2完成方法并释放锁。现在,C1获得了锁。你猜怎么着,幸运的是我们有一个 虽然 循环,因为c1执行循环检查(guard),并且被阻止从缓冲区中删除不存在的元素(c2已经得到它!)如果我们没有 虽然 我们会得到一个 IndexArrayOutOfBoundsException 当c1试图从缓冲区中移除第一个元素时!

    现在,

    好吧,现在为什么我们需要通知所有人?

    在上面的生产者/消费者示例中,我们似乎可以摆脱 通知 . 看来是这样的,因为我们可以证明 等待 生产者和消费者的循环是互斥的。也就是说,看起来我们不能让线程在 put 方法以及 get 方法,因为要使其为真,则必须满足以下条件:

    buf.size() == 0 AND buf.size() == MAX_SIZE (假设最大尺寸不是0)

    但是,这还不够好,我们需要使用 通知所有 . 让我们看看为什么…

    假设我们有一个大小为1的缓冲区(以使示例易于理解)。以下步骤导致我们陷入僵局。请注意,每当一个线程被通知唤醒时,它可以被JVM非确定性地选择——也就是说,任何等待的线程都可以被唤醒。还要注意的是,当多个线程在一个方法的入口阻塞时(即试图获取一个锁),获取的顺序可能是不确定的。还请记住,线程在任何时候都只能在其中一个方法中-同步方法只允许一个线程执行(即持有类中任何(同步)方法的锁)。如果发生以下事件序列-死锁结果:

    步骤1:
    -p1将1个字符放入缓冲区

    步骤2:
    -P2尝试 -检查等待循环-已有一个字符-等待

    步骤3:
    -P3尝试 -检查等待循环-已有一个字符-等待

    步骤4:
    -c1尝试获取1个字符
    -c2尝试在进入 得到 方法
    -c3尝试在进入 得到 方法

    步骤5:
    -C1正在执行 得到 方法-获取char,调用 通知 退出方法
    - 通知 唤醒P2
    -但是,c2在p2可以之前进入方法(p2必须重新获得锁),因此p2在进入到 方法
    -c2检查等待循环,缓冲区中不再有字符,所以等待
    -c3在c2之后进入方法,但在p2之前,检查等待循环,缓冲区中不再有字符,所以等待

    步骤6:
    -现在:有P3、C2和C3在等待!
    -最后,p2获取锁,在缓冲区中放置一个字符,调用notify,退出方法

    步骤7:
    -p2的通知唤醒p3(记住任何线程都可以被唤醒)
    -p3检查等待循环条件,缓冲区中已经有一个字符,所以等待。
    -不再调用notify线程,三个线程永久挂起!

    解决方案:更换 通知 具有 通知所有 在生产商/消费者代码中(如上)。

        3
  •  37
  •   David Crow    17 年前

    有用的区别:

    • 使用 通知() 如果所有等待线程都是可互换的(它们唤醒的顺序无关紧要),或者如果您只有一个等待线程。一个常见的例子是用于从队列执行作业的线程池——添加作业时,会通知其中一个线程唤醒、执行下一个作业并返回睡眠状态。

    • 使用 通知() 对于其他情况,等待线程可能有不同的用途,并且应该能够并发运行。一个例子是对共享资源的维护操作,其中多个线程在访问资源之前等待操作完成。

        4
  •  18
  •   andyuk    17 年前

    我认为这取决于如何生产和消耗资源。如果同时有5个工作对象可用,并且您有5个使用者对象,那么使用notifyall()唤醒所有线程是有意义的,这样每个线程都可以处理1个工作对象。

    如果您只有一个可用的工作对象,那么唤醒所有的消费者对象来争夺那个对象有什么意义呢?第一个检查可用工作的线程将得到它,所有其他线程将检查并发现它们与之无关。

    我找到了一个 great explanation here . 简而言之:

    通常使用notify()方法 对于 资源池 那里 是任意数量的“消费者” 或者“工人”,需要资源,但是 将资源添加到池中时, 只有一个等待的消费者或 工人们可以处理。这个 notifyAll()方法实际用于 大多数其他情况。严格地说,它是 要求通知服务员 允许多个 服务员继续。但这经常 很难知道。所以作为一个将军 规则, 如果你没有特别的 使用notify()的逻辑,然后 应该使用notifyall()。 , 因为通常很难知道 确切地说什么线程将等待 关于一个特定的对象和原因。

        5
  •  11
  •   Gray droiddeveloper    13 年前

    注意,对于并发实用程序,您还可以在 signal() signalAll() 因为这些方法在那里被调用。所以这个问题即使在 java.util.concurrent .

    DougLea提出了一个有趣的观点 famous book 如果是 notify() Thread.interrupt() 同时,通知可能会丢失。如果这可能发生并具有重大影响 notifyAll() 是一个更安全的选择,即使你付出了开销的代价(大多数时候唤醒太多线程)。

        6
  •  10
  •   Steve McLeod    17 年前

    从Joshua Bloch,Java大师在有效的Java第二版中:

    “第69项:首选并发实用程序等待并通知”。

        7
  •  9
  •   Peter Perháč    10 年前

    下面是一个例子。运行它。然后将notifyAll()中的一个更改为notify(),并查看会发生什么。

    ProducerConsumerExample类

    public class ProducerConsumerExample {
    
        private static boolean Even = true;
        private static boolean Odd = false;
    
        public static void main(String[] args) {
            Dropbox dropbox = new Dropbox();
            (new Thread(new Consumer(Even, dropbox))).start();
            (new Thread(new Consumer(Odd, dropbox))).start();
            (new Thread(new Producer(dropbox))).start();
        }
    }
    

    Dropbox类

    public class Dropbox {
    
        private int number;
        private boolean empty = true;
        private boolean evenNumber = false;
    
        public synchronized int take(final boolean even) {
            while (empty || evenNumber != even) {
                try {
                    System.out.format("%s is waiting ... %n", even ? "Even" : "Odd");
                    wait();
                } catch (InterruptedException e) { }
            }
            System.out.format("%s took %d.%n", even ? "Even" : "Odd", number);
            empty = true;
            notifyAll();
    
            return number;
        }
    
        public synchronized void put(int number) {
            while (!empty) {
                try {
                    System.out.println("Producer is waiting ...");
                    wait();
                } catch (InterruptedException e) { }
            }
            this.number = number;
            evenNumber = number % 2 == 0;
            System.out.format("Producer put %d.%n", number);
            empty = false;
            notifyAll();
        }
    }
    

    消费阶层

    import java.util.Random;
    
    public class Consumer implements Runnable {
    
        private final Dropbox dropbox;
        private final boolean even;
    
        public Consumer(boolean even, Dropbox dropbox) {
            this.even = even;
            this.dropbox = dropbox;
        }
    
        public void run() {
            Random random = new Random();
            while (true) {
                dropbox.take(even);
                try {
                    Thread.sleep(random.nextInt(100));
                } catch (InterruptedException e) { }
            }
        }
    }
    

    生产者阶级

    import java.util.Random;
    
    public class Producer implements Runnable {
    
        private Dropbox dropbox;
    
        public Producer(Dropbox dropbox) {
            this.dropbox = dropbox;
        }
    
        public void run() {
            Random random = new Random();
            while (true) {
                int number = random.nextInt(10);
                try {
                    Thread.sleep(random.nextInt(100));
                    dropbox.put(number);
                } catch (InterruptedException e) { }
            }
        }
    }
    
        8
  •  8
  •   ansd    10 年前

    简短摘要:

    总是喜欢 通知() 结束 通知() 除非您有一个大规模并行应用程序,其中大量线程都执行相同的操作。

    说明:

    通知() […]唤醒一个 线程。因为 通知() 不允许您指定 醒来后,只有在大量并行应用程序中才有用 也就是说,有大量线程的程序,都在做类似的工作。 在这样的应用程序中,您不关心哪个线程被唤醒。

    来源: https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

    比较 通知() 具有 通知() 在上面描述的情况下:线程执行相同操作的大规模并行应用程序。如果你打电话 通知() 在那种情况下, 通知() 将导致大量线程的唤醒(即调度),其中许多线程是不必要的(因为实际上只有一个线程可以继续,即将被授予对象监视器的线程) WAIT() , 通知() 通知() 因此浪费了计算资源。

    因此,如果您没有一个应用程序,其中有大量线程同时执行相同的操作,那么最好 通知() 结束 通知() . 为什么?因为,正如其他用户在本论坛中已经回答的那样, 通知()

    唤醒正在等待此对象监视器的单个线程。[…] 选择是 任意的 并由 实施。

    来源:Java SE8API https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#notify-- )

    假设您有一个生产者-消费者应用程序,其中消费者已经准备好了(即 WAIT() 为了消费,生产者准备好了(即 WAIT() 生产和项目队列(待生产/消费)为空。在那种情况下, 通知() 可能只会唤醒消费者,而不会唤醒生产者,因为唤醒者的选择是 任意的 . 虽然生产者和消费者已经准备好分别生产和消费,但生产者-消费者循环不会取得任何进展。相反,消费者被唤醒(即离开 WAIT() 状态),不会将项目从队列中取出,因为它是空的,并且 通知() 另一个消费者继续。

    相反, 通知() 唤醒生产者和消费者。计划的对象取决于计划程序。当然,根据调度程序的实现,调度程序也可能只调度使用者(例如,如果将使用者线程分配为非常高的优先级)。然而,这里的假设是,调度程序只调度使用者的危险性低于JVM只唤醒使用者的危险性,因为任何合理实现的调度程序都不会使 任意的 决定。相反,大多数调度器实现至少做出了一些努力来防止饥饿。

        9
  •  5
  •   NickV    13 年前

    我很惊讶没有人提到臭名昭著的“迷路唤醒”问题(google it)。

    基本上:

    1. 如果有多个线程在相同的条件下等待,
    2. 可以使您从状态A过渡到状态B的多个线程,
    3. 可以使您从状态B转换到状态A的多个线程(通常与1中的线程相同),以及,
    4. 从状态A到状态B的转换应通知1中的线程。

    那么你应该使用notifyall,除非你有可证明的保证,失去唤醒是不可能的。

    一个常见的例子是并发FIFO队列,其中: 多个排队者(1.3。上面)可以将队列从空队列转换为非空队列 多个出局者(2.上面)可以等待条件“队列不为空” 空->非空应通知出局者

    您可以轻松地编写一个交叉操作,其中,从空队列开始,2个排队者和2个排队者交互,1个排队者将保持睡眠。

    这是一个可以与死锁问题相提并论的问题。

        10
  •  5
  •   pardeep131085    11 年前

    我希望这能消除一些疑问。

    notify() :notify()方法唤醒一个等待的线程 对于锁(在该锁上调用wait()的第一个线程)。

    notifyAll() :notifyall()方法唤醒所有等待锁的线程;jvm从等待锁的线程列表中选择一个线程并唤醒 那根线。

    如果是单螺纹 等待锁定时,notify()和notifyall()之间没有显著差异。但是,当有多个线程在等待锁时,在notify()和notifyall()中,实际唤醒的线程是 在合资公司的控制下 而且您不能通过编程控制唤醒特定线程。

    乍一看,只调用notify()来唤醒一个线程似乎是个好主意;似乎不需要唤醒所有线程。但是,问题在于 notify()是唤醒的线程可能不是合适的线程 被唤醒(线程可能正在等待其他条件,或者该条件仍然不满足于该线程等)。 在那种情况下 ,notify()可能会丢失,没有其他线程可能会唤醒,导致死锁类型(通知丢失,所有其他线程都在等待notificationforer)。

    为了避免这个问题 ,当有多个线程在等待锁(或在多个条件下完成等待)时,最好调用notifyall()。notifyAll()方法会唤醒所有线程,因此效率不高。然而,在实际应用中,这种性能损失是可以忽略的。

        11
  •  4
  •   Spoike Otávio Décio    17 年前

    notify() 将唤醒一个线程 notifyAll() 会唤醒一切。据我所知,没有中间地带。但如果你不确定 通知() 会影响你的线程,使用 通知() . 每一次都像是一种魅力。

        12
  •  4
  •   Søren Mors    17 年前

    据我所知,以上所有答案都是正确的,所以我要告诉你一些其他的事情。对于生产代码,您确实应该使用java.util.concurrent中的类。在Java的并发性方面,它们几乎不能为您做什么。

        13
  •  4
  •   Vinh Nguyen    8 年前

    这个答案是对优秀答案的图形重写和简化 xagyg ,包括评论 eran .

    为什么要使用notifyall,即使每个产品都是针对单个消费者的?

    考虑生产者和消费者,简化如下。

    生产者:

    while (!empty) {
       wait() // on full
    }
    put()
    notify()
    

    消费者:

    while (empty) {
       wait() // on empty
    }
    take()
    notify()
    

    假设两个生产者和两个消费者共享大小为1的缓冲区。下图描述了导致 死锁 ,如果使用所有线程,则可以避免 通知所有 .

    每个通知都用被唤醒的线程标记。

    deadlock due to notify

        14
  •  3
  •   Alexander Ryzhov    12 年前

    notify() 允许您编写比 notifyAll() .

    考虑从多个并行线程执行以下代码:

    synchronized(this) {
        while(busy) // a loop is necessary here
            wait();
        busy = true;
    }
    ...
    synchronized(this) {
        busy = false;
        notifyAll();
    }
    

    它可以通过使用 通知() :

    synchronized(this) {
        if(busy)   // replaced the loop with a condition which is evaluated only once
            wait();
        busy = true;
    }
    ...
    synchronized(this) {
        busy = false;
        notify();
    }
    

    在这种情况下,如果您有大量线程,或者等待循环条件的评估成本很高, 通知() 会明显快于 通知() . 例如,如果您有1000个线程,那么999个线程将在第一个线程之后被唤醒并进行计算 通知() ,然后是998,然后是997,依此类推。相反,用 通知() 解决方案,只有一个线程将被唤醒。

    使用 通知() 当需要选择下一个工作将由哪个线程执行时:

    synchronized(this) {
        while(idx != last+1)  // wait until it's my turn
            wait();
    }
    ...
    synchronized(this) {
        last = idx;
        notifyAll();
    }
    

    最后,重要的是要了解 通知() ,里面的代码 synchronized 已唤醒的块将按顺序执行,而不是一次全部执行。假设在上面的示例中有三个线程在等待,第四个线程调用 通知() . 所有三个线程都将被唤醒,但只有一个线程将开始执行并检查 while 循环。如果条件是 true 它会呼叫 wait() 同样,只有这样第二个线程才会开始执行并检查其 虽然 循环条件,等等。

        15
  •  3
  •   Steve    12 年前

    下面是一个简单的解释:

    正确的做法是,无论使用notify()还是notifyall(),立即的结果是,只有另外一个线程会获取监视器并开始执行。(假设在wait()上有一些线程被阻塞了,其他无关的线程并没有吸收所有可用的核心,等等)影响会在稍后发生。

    假设线程A、B和C正在等待这个对象,线程A得到监视器。区别在于一旦a释放监视器会发生什么。如果您使用notify(),那么b和c在wait()中仍然被阻塞:它们没有在监视器上等待,而是在等待通知。当A释放监视器时,B和C仍将坐在那里,等待通知()。

    如果您使用notifyall(),那么b和c都已经超过了“等待通知”状态,并且都在等待获取监视器。当a释放监视器时,b或c将获取它(假设没有其他线程竞争该监视器)并开始执行。

        16
  •  3
  •   S_R    8 年前

    线程有三种状态。

    1. 等待-线程没有使用任何CPU周期
    2. 阻塞-线程在试图获取监视器时被阻塞。它可能仍在使用CPU周期。
    3. 正在运行-线程正在运行。

    现在,当调用notify()时,jvm会选择一个线程,并将它们移动到阻塞状态,从而移动到运行状态,因为监视器对象没有竞争。

    当调用notifyall()时,jvm将挑选所有线程并将它们全部移动到阻塞状态。所有这些线程都将优先获得对象的锁,首先能够获取监视器的线程将首先进入运行状态,依此类推。

        17
  •  3
  •   rajya vardhan    8 年前

    取自 blog 有效Java:

    The notifyAll method should generally be used in preference to notify. 
    
    If notify is used, great care must be taken to ensure liveness.
    

    所以,我所理解的是(从前面提到的博客中,“yann tm”在 accepted answer 和爪哇 docs ):

    • notify():jvm唤醒此对象上的一个等待线程。任意选线,不公平。所以同一根线可以一次又一次地被唤醒。所以系统的状态改变了,但没有真正的进展。因此创建一个 livelock .
    • notifyall():jvm会唤醒所有线程,然后所有线程都会争夺此对象上的锁。现在,CPU调度程序选择一个获取该对象锁的线程。这个选择过程比JVM选择要好得多。因此,确保活力。
        18
  •  2
  •   Aniket Kulkarni    13 年前

    看看@xagyg发布的代码。

    假设两个不同的线程在等待两个不同的条件:
    这个 第一线程 正在等待 buf.size() != MAX_SIZE 第二线程 正在等待 buf.size() != 0 .

    假设在某个时刻 buf.size() 不等于0 . JVM调用 notify() 而不是 notifyAll() ,并通知第一个线程(而不是第二个线程)。

    第一根线被唤醒,检查 Buff.siz() 可能会回来 MAX_SIZE 然后回到等待状态。第二个线程没有被唤醒,继续等待并且不调用 get() .

        19
  •  1
  •   Bhesh Gurung AlexR    13 年前

    notify() 唤醒调用的第一个线程 wait() 在同一个对象上。

    notifyAll() 唤醒所有调用 WAIT() 在同一个对象上。

    最高优先级的线程将首先运行。

        20
  •  1
  •   AKS    12 年前

    我想提一下在实践中Java并发解释了什么:

    第一点,是通知还是通知全部?

    It will be NotifyAll, and reason is that it will save from signall hijacking.
    

    如果两个线程A和B正在等待不同的条件谓词 在相同的条件下,队列和通知被调用,然后由jvm 哪个线程JVM将通知。

    现在,如果通知是针对线程A和JVM通知的线程B的,那么 线程B将唤醒并看到此通知无效,因此 它将再次等待。A线永远不会知道这件事 错过信号,有人劫持了它的通知。

    因此,调用notifyall将解决此问题,但它将再次解决 性能影响,因为它将通知所有线程,所有线程将 竞争相同的锁,它将涉及上下文切换,因此 CPU上的负载。但我们只关心表演 行为正确,如果行为本身不正确,那么 性能是没有用的。

    这个问题可以通过使用JDK5中提供的显式锁的条件对象来解决,因为它为每个条件谓词提供了不同的等待。在这里,它将正确运行,不会出现性能问题,因为它将调用信号,并确保只有一个线程在等待该条件。

        21
  •  1
  •   Saurabh    11 年前

    notify将只通知一个处于等待状态的线程,notify all将立即通知处于等待状态的所有线程,所有通知的线程和所有被阻止的线程都符合锁定条件,其中只有一个线程将获得锁定,所有其他线程(包括之前处于等待状态的线程)将处于被阻止状态。

        22
  •  1
  •   mpromonet    10 年前

    notify() -从对象的等待集中选择一个随机线程并将其放入 BLOCKED 状态。对象等待集中的其余线程仍在 WAITING 状态。

    notifyAll() -将对象的等待集中的所有线程移动到 此路不通 状态。使用后 通知() ,共享对象的等待集中没有剩余线程,因为它们现在都在 此路不通 状态而不是 等待 状态。

    此路不通 -锁定获取。 等待 -正在等待通知(或阻止加入完成)。

        23
  •  0
  •   KingCode    10 年前

    综上所述,以我能想到的最简单的方式,这是由于JVM内置监视器的局限性,它1)在整个同步单元(块或对象)上获取,2)不区分等待/通知/关于的特定条件。

    这意味着,如果多个线程在不同的条件下等待,并且使用了notify(),则选定的线程可能不是在新满足的条件下会取得进展的线程,从而导致该线程(以及其他当前仍在等待的、能够满足该条件的线程等)无法取得进展,并且ev经常挨饿或程序挂断。

    相反,notifyall()允许所有等待线程最终重新获取锁并检查其各自的条件,从而最终允许进行进程。

    因此,只有在保证任何等待线程允许进行进程的情况下,才可以安全地使用notify(),通常,当同一监视器中的所有线程仅检查一个和相同的条件时都会满足这一要求—在现实世界的应用程序中这是非常罕见的情况。

        24
  •  0
  •   Deepak sai    9 年前

    当您调用“object”(期望获得对象锁)的wait()时,intern将释放该对象上的锁,帮助其他线程锁定该“object”,在这种情况下,将有一个以上的线程等待“resource/object”(考虑到其他线程也对相同的上述对象发出了等待)接下来会有一个线程填充资源/对象并调用notify/notifyall)。

    在这里,当您发出同一对象的通知(来自进程/代码的同一侧/另一侧)时,这将释放一个被阻塞并等待的单线程(不是所有等待的线程——这个释放的线程将由JVM线程调度程序选择,并且对象上的所有锁获取进程与常规线程相同)。

    如果只有一个线程将共享/处理此对象,那么可以在wait notify实现中单独使用notify()方法。

    如果您处于一个以上的线程根据您的业务逻辑对资源/对象进行读写的情况,那么您应该使用notifyall()。

    现在,我正在查看当我们对一个对象发出notify()时,JVM是如何准确地标识和破坏等待线程的…

        25
  •  0
  •   jzl106    8 年前

    虽然上面有一些可靠的答案,但我对我读到的困惑和误解的数量感到惊讶。这可能证明了这样一种观点,即应该尽可能多地使用java.util.concurrent,而不是试图编写自己的中断的并发代码。 回到问题:总结一下,今天的最佳实践是避免在所有情况下由于丢失唤醒问题而出现notify()。任何不理解这一点的人都不应该被允许编写关键任务并发代码。如果您担心放牧问题,一次唤醒一个线程的一个安全方法是: 1。为等待线程建立一个显式的等待队列; 2。让队列中的每个线程等待它的前一个线程; 三。完成后让每个线程调用notifyall()。 或者可以使用java. UTI.Orth.*,它已经实现了这一点。

        26
  •  -2
  •   Abhay Bansal    15 年前

    在这里,醒来没有什么意义。 等待notify和notify all,所有这些都在拥有对象监视器之后放置。如果一个线程处于等待阶段并调用了notify,那么这个线程将占用锁,此时没有其他线程可以占用该锁。所以不能同时访问。据我所知,任何等待通知和notifyall调用都只能在获取对象的锁之后进行。如果我错了就纠正我。