如何在 Java 中通知特定线程

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/43289395/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 07:18:08  来源:igfitidea点击:

How to notify a specific thread in Java

javamultithreadingipcjava-threads

提问by Muniyasamy V

How I can call a particular thread in inter-thread communication?

如何在线程间通信中调用特定线程?

In the program below I have two threads t1and t2.

在下面的程序中,我有两个线程t1t2.

When I call t1.notify()it raises:

当我调用t1.notify()它时:

Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at Shared.methodTwo(NotifyThread.java:43)
    at Thread2.run(NotifyThread.java:77)
Error 


class Shared {

    Thread1 t1 ;
    Thread2 t2 ;

    void ThreadInit( Thread1 t1 , Thread2 t2 ) {
        this.t1 = t1 ;
        this.t2 = t2 ;
    }

    synchronized void methodOne()
    {
        Thread t = Thread.currentThread();

        System.out.println(t.getName()+" is relasing the lock and going to wait");

        try
        {
            wait();        //releases the lock of this object and waits
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }

        System.out.println(t.getName()+" got the object lock back and can continue with it's execution");
    }

    synchronized void methodTwo()
    {
        Thread t = Thread.currentThread();

        try
        {
            Thread.sleep(5000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }

        t1.notify();     

        System.out.println("A thread which is waiting for lock of this object is notified by "+t.getName());
    }
    }

    class Thread1 extends Thread 
    {
    Shared s ;
    Thread1( Shared s ) {

        this.s = s ;
    }

    public void run()
            {
                s.methodOne();   //t1 calling methodOne() of 's' object
            }

    } 

    class Thread2 extends Thread {
         Shared s ;
    Thread2( Shared s ) {

        this.s = s ;

    }

    public void run()
            {
                s.methodTwo();   //t1 calling methodOne() of 's' object
            }


    }
    public class NotifyThread 
    {
    public static void main(String[] args)
    {
        final Shared s = new Shared();

        Thread1 t1 = new Thread1(s) ;
        Thread2 t2 = new Thread2(s) ;

        s.ThreadInit(t1,t2) ;

        t1.start();
        t2.start();
    }
}

回答by Stephen C

You don't / can't notify a specific thread. You call notify()on a lock object. This wakes up one of the threads1that is waiting on the lock. In your case, the lock object is a Thread... which rather confuses the picture. However, see below.

您不能/不能通知特定线程。你调用notify()一个锁对象。这会唤醒正在等待锁的线程1之一。在您的情况下,锁定对象是Thread... 这使图片变得混乱。但是,请参见下文。

But your problem (the IllegalMonitorStateException) happens because the thread doing the notifying (i.e. the currentthread) does not hold the lock. It is a (hard) requirement that the current thread must hold the lock when it notifies a lock.

但是您的问题(the IllegalMonitorStateException)发生是因为执行通知的线程(即当前线程)没有持有锁。当前线程在通知锁时必须持有锁是一个(硬)要求。

For more details, read the javadocs for Object.wait(timeout)or (for example) this: http://howtodoinjava.com/core-java/multi-threading/how-to-work-with-wait-notify-and-notifyall-in-java/

有关更多详细信息,请阅读Object.wait(timeout)或(例如)的 javadocs :http: //howtodoinjava.com/core-java/multi-threading/how-to-work-with-wait-notify-and-notifyall-in-java /

1 - If multiple threads are waiting on your lock, one thread is chosen "randomly" by the scheduler. Alternatively notifyAll will wake up all of the waiting threads.

1 - 如果多个线程正在等待您的锁,调度程序将“随机”选择一个线程。或者,notifyAll 将唤醒所有等待的线程。



I would NOT use a Threadobject as a lock object. It will possibly work, but there is also a chance that something else (maybe something in the runtime system) is also locking / waiting / notifying the Threadobjects. Then things would get very confusing.

我不会使用Thread对象作为锁定对象。它可能会工作,但也有可能其他东西(可能是运行时系统中的东西)也在锁定/等待/通知Thread对象。那么事情就会变得非常混乱。

(Indeed, read the javadocfor Thread.join(long)!)

(事实上,阅读的javadocThread.join(long)!)

It is BETTER to create lock objects specifically for this purpose; e.g.

最好为此专门创建锁对象;例如

private final Object lock = new Object();


Also, writing classes that extend Threadis usually a bad idea. It is usually better to implement the Runnableinterface, instantiate it, and pass the instance as a parameter to the Threadconstructor; e.g.

此外,编写扩展的类Thread通常是一个坏主意。通常最好实现Runnable接口,实例化它,然后将实例作为参数传递给Thread构造函数;例如

Thread t = new Thread(new Runnable() {
    public void run() {
        System.out.println("Hello world");
    }});
t.start();

One advantage of implementing Runnablerather than extending Threadis that you can use your code more easily with something that manages the thread life cycles for you; e.g. an ExecutorService, a fork-join thread pool or a classic thread pool.

实现Runnable而不是扩展的一个优点Thread是,您可以通过为您管理线程生命周期的东西更轻松地使用您的代码;例如ExecutorService,一个 fork-join 线程池或经典线程池。

A second one is that light-weight thread logic can be implemented concisely as an anonymous class ... as in my example.

第二个是轻量级线程逻辑可以简洁地实现为匿名类......就像我的例子一样。

回答by Nathan Hughes

To add some points;

补充几点;

Your code is using intrinsic locks. Every object in the JVM has its own lock. This lock has nothing to do with the functionality of the object.Acquiring the lock by itself does nothing (in the absence of further measures like using the synchronized keyword) to prevent other threads from monkeying with the object's contents. Calling notify on a thread does not mean that that particular thread will receive a notification.

您的代码正在使用内在锁。JVM 中的每个对象都有自己的锁。这个锁与对象的功能无关。获取锁本身并没有做任何事情(在没有使用synchronized 关键字等进一步措施的情况下)来防止其他线程与对象的内容发生冲突。在线程上调用 notify 并不意味着该特定线程将收到通知。

As said previously acquiring the lock on Thread objects is discouraged. The join method on Thread uses the intrinsic lock on the thread joined to. If code acquires a lock for different reasons then a thread can be notified for some condition that it may not care about.

如前所述,不鼓励获取 Thread 对象上的锁。Thread 上的 join 方法使用连接到的线程上的内在锁。如果代码出于不同原因获取锁,则可以通知线程可能不关心的某些情况。

The intrinsic lock is a go-between that tells the OS scheduler which threads are waiting. The OS scheduler decides which threads in the wait set for the lock get notified. When a thread calls notify on an object, it is telling the lock on that object to tell the scheduler to choose which waiting thread gets notified. The lock knows which threads are waiting but it doesn't know what condition they're waiting for. (ReentrantLock is a big improvement for this, see the API doc for Condition.)

内在锁是一个中间人,它告诉操作系统调度程序哪些线程正在等待。操作系统调度程序决定等待锁定的哪些线程得到通知。当线程调用对象上的通知时,它告诉该对象上的锁告诉调度程序选择哪个等待线程得到通知。锁知道哪些线程在等待,但不知道它们在等待什么条件。(ReentrantLock 对此进行了很大的改进,请参阅条件的 API 文档。)

Of course notifyAll wakes up all the threads in the wait set, but again that is something the lock and the scheduler know. The thread calling notifyAll doesn't know about what threads are waiting. The system is designed intentionally so that threads cannot notify other threads directly.

当然,notifyAll 会唤醒等待集中的所有线程,但这也是锁和调度程序知道的。调用notifyAll 的线程不知道哪些线程正在等待。该系统是有意设计的,以便线程不能直接通知其他线程。

Yet another thing here is that calling wait with no check for a condition is unreliable. If a thread doesn't happen to have acquired a lock before a notification is made, it misses that notification. If a thread receives a notification, that is no guarantee it will get the lock next, another thread could act and invalidate the state that the notified thread is expecting. Always have some internal state that the current thread can check to verify that the object's state is what the thread is expecting, and make that check as the test in a loop.

还有一件事是,在不检查条件的情况下调用 wait 是不可靠的。如果线程在发出通知之前没有获得锁,它就会错过该通知。如果一个线程收到一个通知,那并不能保证它接下来会获得锁,另一个线程可能会采取行动并使被通知线程期望的状态无效。总是有一些当前线程可以检查的内部状态,以验证对象的状态是线程所期望的,并将该检查作为循环中的测试。

For instance if I have a fixed size blocking queue (implemented using a list internally, protecting it from concurrent access using synchronization) where threads try to take something from the queue but block if the queue is empty, the take method could look like:

例如,如果我有一个固定大小的阻塞队列(在内部使用列表实现,使用同步保护它免受并发访问),其中线程尝试从队列中获取某些内容,但如果队列为空则阻塞,则 take 方法可能如下所示:

public synchronized T take() throws InterruptedException {
    while (list.isEmpty()) {
        wait();
    }
    notifyAll();
    return list.remove(0);
}

Once a waiting thread wakes and reacquires the lock, it checks to see if the current situation is what it has been waiting for. Only if that is the case should the thread exit the loop and proceed.

一旦等待线程唤醒并重新获取锁,它就会检查当前情况是否是它一直在等待的情况。只有在这种情况下,线程才应退出循环并继续。

回答by Niraj

Here you can find out the nice example how to use wait and notify or notifyAll() – Niraj 2 days ago
If you are using notify() instead of notifyAll() it will triger only one thread in wait() state with high priority. If you are using notifyAll() it will triger all the thread which was in wait() state.

在这里你可以找到一个很好的例子,如何使用wait和notify或notifyAll()——Niraj 2天前
如果你使用notify()而不是notifyAll(),它只会触发一个处于wait()状态的高优先级线程。如果您正在使用 notifyAll() 它将触发所有处于 wait() 状态的线程。

   package com.construction.house;

import com.construction.labours.LabourCement;
import com.construction.labours.LabourPainting;
import com.construction.labours.LabourPolishMarbel;

public class House implements Runnable{

    public House() {
        // TODO Auto-generated constructor stub
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        House myHouse = new House();
        LabourCement labourCement = new LabourCement(myHouse,"Cement");
        labourCement.start();

        LabourPainting labourPaining = new LabourPainting(myHouse,"Painter");
        labourPaining.start();

        LabourPolishMarbel labourPolish = new LabourPolishMarbel(myHouse,"PolishMan");
        labourPolish.start();

    }
    boolean isPolished = false,isCemented = false,isPaited = false;

    public synchronized void workAsDemand() throws InterruptedException {
        if (!isPolished) {
            isPolished = true;
            System.out.println(Thread.currentThread().getName()+"--->>Polish in progress");
            wait();
            System.out.println(Thread.currentThread().getName()+"--->>Polish Completed");

        }

        else if (!isPaited) {
            System.out.println(Thread.currentThread().getName()+"--->>Painting house in Progress");
            isPaited = true;
            //notify();
            wait();
            System.out.println(Thread.currentThread().getName()+"--->>Painting house in Completed");

        }

        else if (!isCemented) {
            System.out.println(Thread.currentThread().getName()+"---->>Cemented house");
            isCemented = true;
            notifyAll();

        }


    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        try {
            workAsDemand();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}



package com.construction.labours;

public class LabourCement extends Thread {

    public LabourCement() {
        // TODO Auto-generated constructor stub
    }

    public LabourCement(Runnable arg0) {
        super(arg0);
        // TODO Auto-generated constructor stub
    }

    public LabourCement(String arg0) {
        super(arg0);
        // TODO Auto-generated constructor stub
    }

    public LabourCement(ThreadGroup arg0, Runnable arg1) {
        super(arg0, arg1);
        // TODO Auto-generated constructor stub
    }

    public LabourCement(ThreadGroup arg0, String arg1) {
        super(arg0, arg1);
        // TODO Auto-generated constructor stub
    }

    public LabourCement(Runnable arg0, String arg1) {
        super(arg0, arg1);
        // TODO Auto-generated constructor stub
    }

    public LabourCement(ThreadGroup arg0, Runnable arg1, String arg2) {
        super(arg0, arg1, arg2);
        // TODO Auto-generated constructor stub
    }

    public LabourCement(ThreadGroup arg0, Runnable arg1, String arg2, long arg3) {
        super(arg0, arg1, arg2, arg3);
        // TODO Auto-generated constructor stub
    }


}



package com.construction.labours;

public class LabourPolishMarbel extends Thread {

    public LabourPolishMarbel() {
        // TODO Auto-generated constructor stub
    }

    public LabourPolishMarbel(Runnable arg0) {
        super(arg0);
        // TODO Auto-generated constructor stub
    }

    public LabourPolishMarbel(String arg0) {
        super(arg0);
        // TODO Auto-generated constructor stub
    }

    public LabourPolishMarbel(ThreadGroup arg0, Runnable arg1) {
        super(arg0, arg1);
        // TODO Auto-generated constructor stub
    }

    public LabourPolishMarbel(ThreadGroup arg0, String arg1) {
        super(arg0, arg1);
        // TODO Auto-generated constructor stub
    }

    public LabourPolishMarbel(Runnable arg0, String arg1) {
        super(arg0, arg1);
        // TODO Auto-generated constructor stub
    }

    public LabourPolishMarbel(ThreadGroup arg0, Runnable arg1, String arg2) {
        super(arg0, arg1, arg2);
        // TODO Auto-generated constructor stub
    }

    public LabourPolishMarbel(ThreadGroup arg0, Runnable arg1, String arg2, long arg3) {
        super(arg0, arg1, arg2, arg3);
        // TODO Auto-generated constructor stub
    }

}




package com.construction.labours;

public class LabourPainting extends Thread {

    public LabourPainting() {
        // TODO Auto-generated constructor stub
    }

    public LabourPainting(Runnable arg0) {
        super(arg0);
        // TODO Auto-generated constructor stub
    }

    public LabourPainting(String arg0) {
        super(arg0);
        // TODO Auto-generated constructor stub
    }

    public LabourPainting(ThreadGroup arg0, Runnable arg1) {
        super(arg0, arg1);
        // TODO Auto-generated constructor stub
    }

    public LabourPainting(ThreadGroup arg0, String arg1) {
        super(arg0, arg1);
        // TODO Auto-generated constructor stub
    }

    public LabourPainting(Runnable arg0, String arg1) {
        super(arg0, arg1);
        // TODO Auto-generated constructor stub
    }

    public LabourPainting(ThreadGroup arg0, Runnable arg1, String arg2) {
        super(arg0, arg1, arg2);
        // TODO Auto-generated constructor stub
    }

    public LabourPainting(ThreadGroup arg0, Runnable arg1, String arg2, long arg3) {
        super(arg0, arg1, arg2, arg3);
        // TODO Auto-generated constructor stub
    }

}