【问题标题】:Sleep a thread until an event is attended in another thread from a different class休眠一个线程,直到另一个类的另一个线程参加了一个事件
【发布时间】:2011-02-17 03:04:04
【问题描述】:

我有一个触发 2 个线程的应用程序,第一个启动另一个类来做一些处理,然后启动一个第三个类来做更多的处理。主类中的第二个线程应该等到第三类中的某个事件完成后再执行其工作。如何做到这一点?

我曾尝试实现一个等待/通知以在两个线程之间共享一个锁对象,但从技术上讲,这不会奏效,因为我发现很难。我可以在班级之间共享一个锁吗?请注意,第 3 类的实例在第 1 类中声明并作为参数传递给第 2 类。我还尝试在第三类中创建布尔值,告诉事件何时完成,然后轮询第二个线程,直到该值为真。这行得通,但不是很理想。 actionListner 也是解决这个问题的更好方法吗?

【问题讨论】:

  • 你说的二等三等,是指二等线和三等线吗?
  • 第二类在第一个线程中执行,但会启动第三类,它可能会或可能不会启动其他线程。

标签: java multithreading wait actionlistener


【解决方案1】:

你遇到了什么问题?正如您所描述的,它应该可以工作。例如,您可以在第 3 个类上实现 2 个方法,这些方法保留一个从第一个类检查的标志,并使用实例作为锁从另一个类设置:

boolean done = false;

public synchronized setDone() {

    done = true;

    this.notifyAll();
}

public synchronized waitUntilDone() {

     while (!done) {

        try {
             this.wait();

        } catch (InterruptedException ignore) {
             // log.debug("interrupted: " + ignore.getMessage());
        }
     }
}

(注意:从内存中输入,未使用 Java 编译检查)

原则上,wait 和 notifyAll 之前的 this. 是不需要的,我发现在这种情况下包含它们会更清楚。

【讨论】:

  • 好吧,我确实在第 3 类中有同步方法,但与您说明的方式不太一样。从第一堂课我开始我的第二个线程并在它的任务方法中说 while(!3rd class.done){ //do nothing } //do processing 我的初始大纲的问题是在线程任务中使用锁定对象方法,有时会出现死锁。我现在试试你的方法。 BRB...
  • 您好 rsp,非常感谢您的建议。我实现了它并且它起作用了(至少有一段时间)。我仍然在后续运行中引用 java.nullpointer。在我的第三堂课中,我有它就像你所说明的那样,如果 !done 强制第二个线程从主类进入睡眠状态(在第二个线程任务方法中,我立即调用 3rdClass.waitUntilDone ,然后我说明它的任务代码(给它它的工作)。我应该这样指定它吗?
  • 谢谢你 - 我建议将它提取到一个单独的类中以供大多数重用。我在这里做了 - codingcraftsman.wordpress.com/2015/07/27/…
【解决方案2】:

使用初始值为 1 的 CountDownLatch。

处理完成后,让第三类调用 countDown()。然后调用线程可以调用 await(),这将阻塞直到处理完成。

【讨论】:

    【解决方案3】:

    您正在解决的问题可以通过使用ActionListener 或共享Queue 轻松解决。

    只需选择任何会阻塞消费者的阻塞队列,直到队列中出现某些内容。简单、清晰且经过验证。

    如果您需要更多,请查看 http://akkasource.org/http://jcp.org/en/jsr/detail?id=166 之类的项目(Java 7 中将默认包含)。

    【讨论】:

    • +1 用于使用阻塞队列。但是,我不确定使用ActionListener 对多线程有多大帮助。毕竟ActionListener只是一个接口。
    • 队列仅在有多个线程产生结果时才有用。否则,使用 Future。如果线程不产生结果,则使用 CountDownLatch。 BTW JSR-166 包含在 Java 1.5 中。
    • 感谢大家的回复。我将探讨所有这些不同的想法,但显而易见的问题仍然存在,哪种方法最好?
    • @rhys keepence:抱歉,我的意思是 JSR166y
    【解决方案4】:

    您想使用计数信号量。条件变量用于在监视器内调度线程。这不是你想要做的。

    您创建一个计数信号量并将计数设置为零

    // create a counting semaphore with an initial count of zero
    java.util.concurrent.Semaphore s = new java.util.concurrent.Semaphore(0);
    

    您将信号量传递给您的班级进行处理。完成后,通过调用 s.release() 将计数增加到 1。

    要阻塞线程直到处理器完成,您调用s.aquire()。该调用将导致您的其他线程阻塞,直到处理器调用s.release()

    这是最简单的解决方案。

    顺便说一句,s.aquire()s.release() 是线程安全的,所以你不需要使用 synchronize 关键字。线程可以共享对信号量的引用并在不锁定的情况下调用其方法。

    更新:

    我将在此处回复您的评论,而不是发表新评论。

    是的,在您的情况下,wait()/notify() 解决方案类似于使用信号量。用信号量重写 rsp 的解决方案,它看起来像:

    java.util.concurrent.Semaphore s = new java.util.concurrent.Semaphore(0);
    
    public setDone() {
        s.release();
    }
    
    public waitUntilDone() {
         s.aquire();
    }
    

    它更简单,您不需要不必要的锁(注意我从方法 decs 中删除了 synchronized 关键字。)。

    条件变量(wait()/notify())和信号量有两个区别。

    区别#1:对 notify() 的调用可能会丢失,对 release() 的调用永远不会丢失

    第一个区别是,如果没有线程通过调用 wait() 等待,则调用 notify() 会丢失。解决方法是在调用 wait() 之前检查条件。基本上,我们需要记住 notify() 是使用共享变量调用的,因此我们不会在 worker 调用 notify() 后意外调用 wait(),否则我们会死锁。无论调用acquire() 和release() 的顺序如何,计数信号量都有效,因为它们在内部维护一个计数。

    区别#2:调用wait()会自动释放锁,调用acquire()不会

    一些背景信息将对此有所帮助。在你的程序中boolean done = false; 变量是条件,但它不是条件变量。令人困惑的术语,我知道。条件变量是具有操作 wait() 和 notify() 的变量。 Java 中的每个对象都有一个隐藏在里面的条件变量和一个对应的锁。

    所有条件变量都与锁相关联。在调用 wait() 和 notify() 之前,您必须获取锁(如果不这样做,您将得到运行时异常,请尝试一下)。一旦获得锁,调用 wait() 会自动释放锁,允许监视器内的另一个线程可能调用 notify()。有时,这正是您想要的,尝试用信号量模拟这种行为会复杂得多。

    注意:我使用的监视器的学术定义与 Java 的监视器定义完全不同。

    【讨论】:

    • 您好,感谢您的回答。试了一下,效果很好。但其工作方式与在布尔变量上使用等待和通知的方式几乎相同。我想我必须运行多次才能发现任何故障。它们有什么主要区别吗?
    【解决方案5】:

    我认为join() 操作适合此目的

    插图:

    假设你有 2 个线程

    thead1 -> 需要等待的线程。

    thread2 -> thread1 必须等待的线程。

    然后在thread1的代码中,在你想等待的地方写

    thread2.join()
    

    希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 2010-09-24
      • 1970-01-01
      • 1970-01-01
      • 2020-04-30
      • 1970-01-01
      • 2014-07-22
      • 2011-10-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多