如何让一个Java线程等待另一个线程的输出?

我正在用一个应用程序逻辑线程和一个数据库访问线程来制作一个Java应用程序。 它们都在应用程序的整个生命周期内持续存在,并且都需要同时运行(一个与服务器对话,一个与用户通话;当应用程序完全启动时,我需要两者都工作)。

但是,在启动时,我需要确保最初的应用程序线程等待数据库线程准备就绪(当前通过轮询自定义方法dbthread.isReady() )。 我不介意应用程序线程阻塞,直到数据库线程准备就绪。

Thread.join()看起来不像解决scheme – 数据库线程只在应用程序closures时退出。

while (!dbthread.isReady()) {}types的工作,但空循环消耗大量的处理器周期。

任何其他的想法? 谢谢。

我真的build议你在神奇的multithreading世界中开始之前,阅读像Sun的Java Concurrency这样的教程。

还有一些很好的书籍(谷歌的“Java并发编程”,“实践中的Java并发”)。

为了得到你的答案:

在你的代码中必须等待dbThread ,你必须有这样的东西:

 //do some work synchronized(objectYouNeedToLockOn){ while (!dbThread.isReady()){ objectYouNeedToLockOn.wait(); } } //continue with work after dbThread is ready 

在你的dbThread的方法中,你需要做这样的事情:

 //do db work synchronized(objectYouNeedToLockOn){ //set ready flag to true (so isReady returns true) ready = true; objectYouNeedToLockOn.notifyAll(); } //end thread run method here 

我在这些例子中使用的objectYouNeedToLockOn最好是你需要从每个线程同时操作的对象,或者你可以为此目的创build一个单独的Object (我不build议使这些方法本身同步):

 private final Object lock = new Object(); //now use lock in your synchronized blocks 

为了进一步理解:
还有其他(有时是更好的)方法来做到上述事情,例如使用CountdownLatches等。从Java 5开始, java.util.concurrent包和子包中有很多漂亮的并发类。 你真的需要在网上find材料来了解并发性,或者得到一本好书。

使用计数器为1的CountDownLatch 。

 CountDownLatch latch = new CountDownLatch(1); 

现在在应用程序线程do-

 latch.await(); 

在db线程中,完成之后,

 latch.countDown(); 

要求::

  1. 等待下一个线程的执行直到上一个完成。
  2. 下一个线程不能启动,直到前一个线程停止,不pipe时间消耗。
  3. 它必须简单易用。

回答::

@查看java.util.concurrent.Future.get()doc。

future.get()如果需要,等待计算完成,然后检索其结果。

任务完成!! 看下面的例子

 import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.junit.Test; public class ThreadTest { public void print(String m) { System.out.println(m); } public class One implements Callable<Integer> { public Integer call() throws Exception { print("One..."); Thread.sleep(6000); print("One!!"); return 100; } } public class Two implements Callable<String> { public String call() throws Exception { print("Two..."); Thread.sleep(1000); print("Two!!"); return "Done"; } } public class Three implements Callable<Boolean> { public Boolean call() throws Exception { print("Three..."); Thread.sleep(2000); print("Three!!"); return true; } } /** * @See java.util.concurrent.Future.get() doc * <p> * Waits if necessary for the computation to complete, and then * retrieves its result. */ @Test public void poolRun() throws InterruptedException, ExecutionException { int n = 3; // Build a fixed number of thread pool ExecutorService pool = Executors.newFixedThreadPool(n); // Wait until One finishes it's task. pool.submit(new One()).get(); // Wait until Two finishes it's task. pool.submit(new Two()).get(); // Wait until Three finishes it's task. pool.submit(new Three()).get(); pool.shutdown(); } } 

这个程序的输出::

 One... One!! Two... Two!! Three... Three!! 

你可以看到在完成比其他线程更大的任务之前需要6秒。 所以Future.get()等待任务完成。

如果您不使用future.get(),则不会等待完成并执行基于时间的消耗。

祝你好运与Java并发。

尝试使用java.util.concurrent包中的CountDownLatch类,该包提供更高级别的同步机制,比任何低级别的东西都要less得多。

您可以使用两个线程之间共享的Exchanger对象来完成此操作:

 private Exchanger<String> myDataExchanger = new Exchanger<String>(); // Wait for thread's output String data; try { data = myDataExchanger.exchange(""); } catch (InterruptedException e1) { // Handle Exceptions } 

而在第二个线程中:

 try { myDataExchanger.exchange(data) } catch (InterruptedException e) { } 

正如其他人所说,不要采取这种轻松,只是复制粘贴代码。 先做一些阅读。

 public class ThreadEvent { private final Object lock = new Object(); public void signal() { synchronized (lock) { lock.notify(); } } public void await() throws InterruptedException { synchronized (lock) { lock.wait(); } } } 

然后像这样使用这个类:

创build一个ThreadEvent:

 ThreadEvent resultsReady = new ThreadEvent(); 

在这个方法中,等待结果:

 resultsReady.await(); 

在创build所有结果之后创build结果的方法中:

 resultsReady.signal(); 

编辑:

(对不起,编辑这篇文章,但这个代码有一个非常糟糕的竞争条件,我没有足够的声誉来评论)

只有在100%确定signal()在await()之后被调用时才能使用它。 这是你不能使用Java对象(例如Windows事件)的一个重要原因。

如果代码按以下顺序运行:

 Thread 1: resultsReady.signal(); Thread 2: resultsReady.await(); 

那么线程2将永远等待 。 这是因为Object.notify()只唤醒当前正在运行的线程之一。 稍后等待的线程不会被唤醒。 这与我期望的事件的工作方式有很大的不同,在这种情况下,一个事件发出信号直到a)等待或b)明确重置。

注意:大多数情况下,您应该使用notifyAll(),但这与上面的“永远等待”问题无关。

来自java.lang.concurrent包的Future接口旨在提供对另一个线程中计算的结果的访问。

看看FutureTask和ExecutorService,做一个现成的做这种事情的方法。

我强烈推荐阅读Java Concurrency In Practice给那些对并发和multithreading感兴趣的人。 它显然集中在Java,但是对于任何使用其他语言的人来说,也是有很多东西的。

很多正确的答案,但没有一个简单的例子..这是一个简单的方法来使用CountDownLatch

 //inside your currentThread.. lets call it Thread_Main //1 final CountDownLatch latch = new CountDownLatch(1); //2 // launch thread#2 new Thread(new Runnable() { @Override public void run() { //4 //do your logic here in thread#2 //then release the lock //5 latch.countDown(); } }).start(); try { //3 this method will block the thread of latch untill its released later from thread#2 latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } //6 // You reach here after latch.countDown() is called from thread#2 

如果你想快速和肮脏的东西,你可以在你的while循环中添加一个Thread.sleep()调用。 如果数据库库是你不能改变的东西,那么真的没有其他简单的解决scheme。 轮询数据库直到准备好等待时间不会中断性能。

 while (!dbthread.isReady()) { Thread.sleep(250); } 

几乎没有什么可以称为优雅的代码,但完成工作。

如果您可以修改数据库代码,那么使用其他答案中提出的互斥锁更好。

这适用于所有语言:

你想有一个事件/侦听器模型。 你创build一个监听器来等待一个特定的事件。 事件将在您的工作线程中创build(或发送)。 这将阻塞线程,直到收到信号,而不是经常轮询,看是否满足条件,就像您当前的解决scheme。

你的情况是造成死锁的最常见的原因之一 – 确保你指示另一个线程,而不pipe可能发生的错误。 示例 – 如果您的应用程序引发exception,并且从不调用方法来告知其他事情已经完成。 这将使得另一个线程从不“醒来”。

我build议您在实施您的案例之前,先看看使用事件和事件处理程序的概念,以更好地理解这个范例。

或者,你可以使用一个阻塞函数调用使用互斥体 – 这将导致线程等待资源是免费的。 要做到这一点,你需要很好的线程同步,比如:

 Thread-A Locks lock-a Run thread-B Thread-B waits for lock-a Thread-A unlocks lock-a (causing Thread-B to continue) Thread-A waits for lock-b Thread-B completes and unlocks lock-b 

您可以从一个线程中的阻塞队列读取数据,并在另一个线程中写入。

以来

  1. join()已被排除
  2. 你已经使用了CountDownLatch和
  3. Future.get()已经被其他专家提出,

你可以考虑其他的select:

  1. ExecutorService 调用全部

     invokeAll(Collection<? extends Callable<T>> tasks) 

    执行给定的任务,返回一份持有其状态和结果的期货清单。

  2. ForkJoinPool或Executors newWorkStealingPool (自Java 8发布以来)

    使用所有可用的处理器创build工作线程池作为其目标并行级别。

在这里输入图像描述

这个想法可以适用? 如果你使用CountdownLatches或者Semaphores,那么完美但是如果你正在寻找面试的最简单的答案,我认为这可以适用。