Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 通知在不同对象中运行的线程_Java_Multithreading_Design Patterns_Concurrency_Thread Safety - Fatal编程技术网

Java 通知在不同对象中运行的线程

Java 通知在不同对象中运行的线程,java,multithreading,design-patterns,concurrency,thread-safety,Java,Multithreading,Design Patterns,Concurrency,Thread Safety,我仔细阅读了Oracle文档,没有找到解决我的问题的设计模式解决方案。我有两个匿名线程,一个需要通知另一个 public static void main(String[] args) { MyClass obj = new MyClass(); obj.a(); obj.b(); } MyClass有两个不同的函数,每个函数启动一个匿名线程。B个人希望被他的妻子A吵醒 public class MyClass{ public MyClass(){

我仔细阅读了Oracle文档,没有找到解决我的问题的设计模式解决方案。我有两个匿名线程,一个需要通知另一个

public static void main(String[] args) {
    MyClass obj = new MyClass();
    obj.a();
    obj.b();

}
MyClass有两个不同的函数,每个函数启动一个匿名线程。B个人希望被他的妻子A吵醒

public class MyClass{

    public MyClass(){

    }

    public void a() {
        new Thread(new Runnable(){

            @Override
            public synchronized void run() {
                System.out.println("A: I am going to sleep");
                try {
                    Thread.sleep(1000);
                    System.out.println("A: I slept one full day. Feels great.");
                    System.out.println("A: Hey B, wake up!");
                    notifyAll();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }


            }
        }).start();


    }

    public void b() {
        new  Thread(new Runnable(){

            @Override
            public synchronized void run() {
                System.out.println("B: I am  going to sleep. A, please wake me up.");
                try {
                    wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("B: Thank you A for waking me up!");


            }
        }).start();


    }

}
不幸的是,B永远睡不着,不能被他的妻子A吵醒

public class MyClass{

    public MyClass(){

    }

    public void a() {
        new Thread(new Runnable(){

            @Override
            public synchronized void run() {
                System.out.println("A: I am going to sleep");
                try {
                    Thread.sleep(1000);
                    System.out.println("A: I slept one full day. Feels great.");
                    System.out.println("A: Hey B, wake up!");
                    notifyAll();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }


            }
        }).start();


    }

    public void b() {
        new  Thread(new Runnable(){

            @Override
            public synchronized void run() {
                System.out.println("B: I am  going to sleep. A, please wake me up.");
                try {
                    wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("B: Thank you A for waking me up!");


            }
        }).start();


    }

}
程序的输出:

A: I am going to sleep
B: I am  going to sleep. A, please wake me up.
A: I slept one full day. Feels great.
A: Hey B, wake up!
我知道A和B在两个不同的匿名线程对象中运行,所以A只能通知其他A(床上没有其他妻子,所以通知功能在这里是无用的)


此问题的正确设计模式是什么

这些线程之间需要共享一个类变量。线程A首先锁定锁,然后要进入睡眠状态,线程B锁定它。螺纹A通过解锁锁来唤醒螺纹B。也可以使用。基本点是在单个对象监视器上调用
wait()
notify()
notifyAll()
,以进行线程同步。我会这样做的

在我的代码中,
MyClass
具有
a()
b()
实例方法
synchronized
。因此,调用这些方法的实例将成为隐式监视器。我正在与2个
可运行的
实现共享
MyClass
(即
obj
)的相同实例

public class MyClass{

    public MyClass(){

    }

    public synchronized void a() {
        System.out.println("A: I am going to sleep");
        try {
            Thread.sleep(5000);
            wait();
            System.out.println("A: I slept one full day. Feels great.");
            System.out.println("A: Hey B, wake up!");
            notifyAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public synchronized void b() {
        System.out.println("B: I am  going to sleep. A, please wake me up.");
        notifyAll();
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("B: Thank you A for waking me up!");

    }

    public static void main(String [] args) {
        MyClass obj = new MyClass();

        Thread t1 = new Thread(new RunnableImpl(obj, true));
        Thread t2 = new Thread(new RunnableImpl(obj, false));
        t1.start();
        t2.start();
    }

}

class RunnableImpl implements Runnable {

    boolean callA;
    MyClass obj;

    public RunnableImpl(MyClass obj, boolean callA) {
        this.callA = callA;
        this.obj = obj;
    }


    @Override
    public void run() {
        if(callA) {
            obj.a();
        }
        else {
            obj.b();
        }
    }

}

您需要线程共享一个公共对象来调用上的wait()/notify()方法。现在您在
this
对象上调用它们,这两种情况下都是它们自己的线程对象


还要注意,您还需要在公共对象上同步,因此您不能只在run()方法上放置synchronized。

为了能够从一个线程唤醒另一个线程,它们需要与公共对象同步。例如,您可以使用调用线程的MyClass对象:

public void a() {
    new Thread(new Runnable(){

        @Override
        public synchronized void run() {
            System.out.println("A: I am going to sleep");
            synchronized(MyClass.this)
            {
                try {
                    Thread.sleep(1000);
                    System.out.println("A: I slept one full day. Feels great.");
                    System.out.println("A: Hey B, wake up!");
                    MyClass.this.notifyAll();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }).start();


}

public void b() {
    new  Thread(new Runnable(){

        @Override
        public synchronized void run() {
            System.out.println("B: I am  going to sleep. A, please wake me up.");
            synchronized(MyClass.this)
            {
                System.out.println("B: Thank you A for waking me up!");
            }
        }
    }).start();


}
这将使
a()
的线程获得锁并休眠1000毫秒。同时会调用
b()
,但它的线程必须等到
a()
的线程释放锁后才能打印
,谢谢你叫醒我


如果您总是在
b()
之前调用
a()
,这将起作用。否则,如果
b()
首先获得了锁,那么
谢谢你叫醒我
将在
a()
睡眠
之前执行

当前,代码中的锁位于两个不同的对象上-由
a
创建的
Runnable
在自身上有一个锁,与
b
相同,因此当调用
notifyAll
时,没有对象等待锁通知

同步块中的
线程.sleep
也有问题

更改您的代码,以便在使用
synchronized
关键字时获得锁,如下所示:

public void a()
{
  new Thread(
    new Runnable()
    {
      @Override
      public void run()
      {
        try
        {
          System.out.println("A: I am going to sleep");
          Thread.sleep(1000);
        }
        catch (InterruptedException e)
        {
          e.printStackTrace();
        }

        synchronized(MyClass.this)
        {
          System.out.println("A: I slept one full day. Feels great.");
          System.out.println("A: Hey B, wake up!");
          MyClass.this.notifyAll();
        }
      }
    }
  ).start();
}

public void b()
{
  new Thread(
    new Runnable()
    {
      @Override
      public void run()
      {
        synchronized(MyClass.this)
        {
          System.out.println("B: I am  going to sleep. A, please wake me up.");

          try
          {
            MyClass.this.wait();
          }
          catch (InterruptedException e)
          {
            e.printStackTrace();
          }

          System.out.println("B: Thank you A for waking me up!");
        }
      }
    }
  ).start();
}

代码中有两个问题

  • 正如其他人所建议的那样。使用时需要使用相同的锁 通知并等待。您正在使用不同的对象来等待和通知 是它们各自的线程实例。 下面的代码是使用MyClass.this运行的

    试一试{ 等待(); }捕捉(中断异常e){

  • 即使使用了正确的锁,代码也存在另一个问题 假设您试图通过线程遇到。在线程A中睡眠(1000)。此问题 被称为错过通知,即threadA可以在threadB执行其wait()方法之前完成,这将导致threadB无限休眠

  • 以上两个问题的解决方案都是使用闩锁。请尝试CountDownLatch 见下文

    import java.util.concurrent.CountDownLatch;
    
    public class MyClass{
    
        CountDownLatch latch = new CountDownLatch(1);
    
        public MyClass(){
    
        }
    
        public void a() {
            new Thread(new Runnable(){
                @Override
                public void run() {
                    System.out.println("A: I am going to sleep");
                    System.out.println("A: I slept one full day. Feels great.");
                    System.out.println("A: Hey B, wake up!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    latch.countDown();
                }
            }).start();
        }
    
        public void b() {
            new  Thread(new Runnable(){
                @Override
                public  void run() {
                    System.out.println("B: I am  going to sleep. A, please wake me up.");
                    try {
                        latch.await();
                    } catch (InterruptedException e) {}
                    System.out.println("B: Thank you A for waking me up!");
                }
            }).start();
        }
    
        public static void main(String[] args) {
            MyClass obj = new MyClass();
            obj.a();
            obj.b();
        }
    
    }
    

    拜托,难道你不能让其他人自己做作业吗?如果你为他们写下工作代码,他们将如何学习。@Kayaman如果OP没有代码,我就不会发布答案。OP尝试了一些东西,所以我发布了一个可能的答案。我想这是怎么回事?@sanbhat为什么我们在b中有notifyAll()呢?
    a()
    b()
    方法在同一监视器下同步
    obj
    。因此
    notifyAll()
    将通知等待
    a()
    b()的所有线程