【问题标题】:How to notify a specific thread in Java如何在 Java 中通知特定线程
【发布时间】:2017-09-03 12:05:22
【问题描述】:

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

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

当我调用 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();
    }
}

【问题讨论】:

标签: java multithreading ipc java-threads


【解决方案1】:

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

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

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

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


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

(确实,请阅读javadoc for Thread.join(long)!)

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

private final Object lock = new Object();

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

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

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

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

【讨论】:

  • 所有这些(这是相当不错的建议)我根本不会使用等待/通知。我会使用 java.util.concurrent 包中的一些类:LockSemaphoreCyclicBarrierall better bets,而不是尝试自己管理同步。
  • @markspace - 这是真的。但在这种情况下,我怀疑 OP 这样做是为了理解等待/通知。
【解决方案2】:

加点;

您的代码正在使用内部锁。 JVM 中的每个对象都有自己的锁。 此锁与对象的功能无关。 获取锁本身并没有任何作用(在没有使用同步关键字等进一步措施的情况下)来防止其他线程对对象的内容进行监视.在线程上调用 notify 并不意味着该特定线程会收到通知。

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

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

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

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

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

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

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

【讨论】:

    【解决方案3】:

    在这里您可以找到如何使用 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
        }
    
    }
    

    【讨论】:

    • 这里你可以找到如何使用 wait 和 notify 或 notifyAll() 的好例子
    • 如果你使用 notify() 而不是 notifyAll() 它将只触发一个处于 wait() 状态的线程,具有高优先级。如果您使用的是 notifyAll() ,它将触发所有处于 wait() 状态的线程。谢谢
    • 添加描述以回答,不在 cmets 中
    【解决方案4】:

    解决方法:移除调用对象t1

    t1.notify() 更改为notify()

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-05
      相关资源
      最近更新 更多