如何让一个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();
要求::
- 等待下一个线程的执行直到上一个完成。
- 下一个线程不能启动,直到前一个线程停止,不pipe时间消耗。
- 它必须简单易用。
回答::
@查看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
您可以从一个线程中的阻塞队列读取数据,并在另一个线程中写入。
以来
-
join()
已被排除 - 你已经使用了CountDownLatch和
- Future.get()已经被其他专家提出,
你可以考虑其他的select:
-
从
ExecutorService
调用全部invokeAll(Collection<? extends Callable<T>> tasks)
执行给定的任务,返回一份持有其状态和结果的期货清单。
-
ForkJoinPool或
Executors
newWorkStealingPool (自Java 8发布以来)使用所有可用的处理器创build工作线程池作为其目标并行级别。
这个想法可以适用? 如果你使用CountdownLatches或者Semaphores,那么完美但是如果你正在寻找面试的最简单的答案,我认为这可以适用。