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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/289434/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 12:37:05  来源:igfitidea点击:

How to make a Java thread wait for another thread's output?

javamultithreading

提问by Piskvor left the building

I'm making a Java application with an application-logic-thread and a database-access-thread. Both of them persist for the entire lifetime of the application and both need to be running at the same time (one talks to the server, one talks to the user; when the app is fully started, I need bothof them to work).

我正在制作一个带有应用程序逻辑线程和数据库访问线程的 Java 应用程序。他们都坚持为应用程序和都需要的整个生命周期,以在同一时间运行(一个会谈到服务器,一个谈判给用户;当应用程序完全启动,我需要两个人工作)。

However, on startup, I need to make sure that initially the app thread waits until the db thread is ready (currently determined by polling a custom method dbthread.isReady()). I wouldn't mind if app thread blocks until the db thread was ready.

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

Thread.join()doesn't look like a solution - the db thread only exits at app shutdown.

Thread.join()看起来不像解决方案 - db 线程仅在应用程序关闭时退出。

while (!dbthread.isReady()) {}kind of works, but the empty loop consumes a lot of processor cycles.

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

Any other ideas? Thanks.

还有其他想法吗?谢谢。

采纳答案by Herman Lintvelt

I would really recommend that you go through a tutorial like Sun's Java Concurrencybefore you commence in the magical world of multithreading.

我真的建议您在进入多线程的神奇世界之前先阅读Sun 的 Java Concurrency 之类的教程。

There are also a number of good books out (google for "Concurrent Programming in Java", "Java Concurrency in Practice".

还有一些好书(谷歌搜索“Java并发编程”,“Java并发实践”。

To get to your answer:

要得到你的答案:

In your code that must wait for the dbThread, you must have something like this:

在您必须等待 的代码中dbThread,您必须具有以下内容:

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

In your dbThread's method, you would need to do something like this:

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

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

The objectYouNeedToLockOnI'm using in these examples is preferably the object that you need to manipulate concurrently from each thread, or you could create a separate Objectfor that purpose (I would not recommend making the methods themselves synchronized):

objectYouNeedToLockOn我使用这些例子是最好的对象,您需要从每个线程并发操作,或者你可以创建一个单独的Object用于该目的(我不建议使自己同步的方法):

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

To further your understanding:
There are other (sometimes better) ways to do the above, e.g. with CountdownLatches, etc. Since Java 5 there are a lot of nifty concurrency classes in the java.util.concurrentpackage and sub-packages. You really need to find material online to get to know concurrency, or get a good book.

为了进一步理解:
还有其他(有时更好)的方法来执行上述操作,例如 withCountdownLatches等。从 Java 5 开始,java.util.concurrent包和子包中有很多漂亮的并发类。你真的需要在网上找资料来了解并发,或者找一本好书。

回答by Klathzazt

This applies to all languages:

这适用于所有语言:

You want to have an event/listener model. You create a listener to wait for a particular event. The event would be created (or signaled) in your worker thread. This will block the thread until the signal is received instead of constantly polling to see if a condition is met, like the solution you currently have.

您想要一个事件/侦听器模型。您创建一个侦听器以等待特定事件。该事件将在您的工作线程中创建(或发出信号)。这将阻塞线程直到接收到信号,而不是不断轮询以查看是否满足条件,就像您目前拥有的解决方案一样。

Your situation is one of the most common causes for deadlocks- make sure you signal the other thread regardless of errors that may have occurred. Example- if your application throws an exception- and never calls the method to signal the other that things have completed. This will make it so the other thread never 'wakes up'.

您的情况是导致死锁的最常见原因之一 - 无论可能发生的错误如何,请确保向另一个线程发出信号。示例 - 如果您的应用程序抛出异常 - 并且从不调用该方法来通知其他人事情已经完成。这将使另一个线程永远不会“醒来”。

I suggest that you look into the concepts of using events and event handlers to better understand this paradigm before implementing your case.

我建议您在实现您的案例之前研究使用事件和事件处理程序的概念,以更好地理解这种范式。

Alternatively you can use a blocking function call using a mutex- which will cause the thread to wait for the resource to be free. To do this you need good thread synchronization- such as:

或者,您可以使用使用互斥锁的阻塞函数调用,这将导致线程等待资源空闲。为此,您需要良好的线程同步 - 例如:

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

回答by WMR

Try CountDownLatchclass out of the java.util.concurrentpackage, which provides higher level synchronization mechanisms, that are far less error prone than any of the low level stuff.

从包中尝试CountDownLatchjava.util.concurrent,它提供更高级别的同步机制,与任何低级别的东西相比,它更不容易出错。

回答by kgiannakakis

You could do it using an Exchangerobject shared between the two threads:

您可以使用在两个线程之间共享的Exchanger对象来做到这一点:

private Exchanger<String> myDataExchanger = new Exchanger<String>();

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

And in the second thread:

在第二个线程中:

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

}

As others have said, do not take this light-hearted and just copy-paste code. Do some reading first.

正如其他人所说,不要轻率地复制粘贴代码。先读点书。

回答by Mario Ortegón

If you want something quick and dirty, you can just add a Thread.sleep() call within your while loop. If the database library is something you can't change, then there is really no other easy solution. Polling the database until is ready with a wait period won't kill the performance.

如果你想要一些快速而肮脏的东西,你可以在你的 while 循环中添加一个 Thread.sleep() 调用。如果数据库库是你无法改变的东西,那么真的没有其他简单的解决方案。轮询数据库直到准备就绪并等待一段时间不会降低性能。

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

Hardly something that you could call elegant code, but gets the work done.

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

In case you can modify the database code, then using a mutex as proposed in other answers is better.

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

回答by Bill Michell

The Futureinterface from the java.lang.concurrentpackage is designed to provide access to results calculated in another thread.

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

Take a look at FutureTaskand ExecutorServicefor a ready-made way of doing this kind of thing.

查看FutureTaskExecutorService以了解执行此类操作的现成方法。

I'd strongly recommend reading Java Concurrency In Practiceto anyone interested in concurrency and multithreading. It obviously concentrates on Java, but there is plenty of meat for anybody working in other languages too.

我强烈建议对并发和多线程感兴趣的任何人阅读Java Concurrency In Practice。它显然专注于 Java,但对于使用其他语言工作的任何人来说,也有很多内容。

回答by pdeva

Use a CountDownLatchwith a counter of 1.

使用计数器为 1的CountDownLatch

CountDownLatch latch = new CountDownLatch(1);

Now in the app thread do-

现在在应用程序线程中执行-

latch.await();

In the db thread, after you are done, do -

在数据库线程中,完成后,请执行-

latch.countDown();

回答by pdeva

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();
        }
    }
}

Use this class like this then:

然后像这样使用这个类:

Create a ThreadEvent:

创建一个线程事件:

ThreadEvent resultsReady = new ThreadEvent();

In the method this is waiting for results:

在方法中这是等待结果:

resultsReady.await();

And in the method that is creating the results after all the results have been created:

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

resultsReady.signal();

EDIT:

编辑:

(Sorry for editing this post, but this code has a very bad race condition and I don't have enough reputation to comment)

(抱歉编辑这篇文章,但这段代码的竞争条件非常糟糕,我没有足够的声誉来评论)

You can only use this if you are 100% sure that signal() is called after await(). This is the one big reason why you cannot use Java object like e.g. Windows Events.

只有在 100% 确定在 await() 之后调用了 signal() 时,才能使用它。这是您不能使用 Java 对象(例如 Windows Events)的一大原因。

The if the code runs in this order:

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

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

then thread 2 will wait forever. This is because Object.notify() only wakes up one of the currently running threads. A thread waiting later is not awoken. This is very different from how I expect events to work, where an event is signalled until a) waited for or b) explicitly reset.

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

Note: Most of the time, you should use notifyAll(), but this is not relevant to the "wait forever" problem above.

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

回答by Ash

Requirement ::

要求 ::

  1. To wait execution of next thread until previous finished.
  2. Next thread must not start until previous thread stops, irrespective of time consumption.
  3. It must be simple and easy to use.
  1. 等待下一个线程的执行,直到上一个线程完成。
  2. 在前一个线程停止之前,下一个线程不得启动,而与时间消耗无关。
  3. 它必须简单易用。

Answer ::

回答 ::

@See java.util.concurrent.Future.get() doc.

future.get() Waits if necessary for the computation to complete, and then retrieves its result.

@参见 java.util.concurrent.Future.get() 文档。

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

Job Done!! See example below

任务完成!!请参阅下面的示例

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();
    }
}

Output of this program ::

这个程序的输出::

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

You can see that takes 6sec before finishing its task which is greater than other thread. So Future.get() waits until the task is done.

您可以看到在完成其任务之前需要 6 秒,这比其他线程更长。所以 Future.get() 一直等到任务完成。

If you don't use future.get() it doesn't wait to finish and executes based time consumption.

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

Good Luck with Java concurrency.

祝 Java 并发好运。

回答by Ingo

You could read from a blocking queue in one thread and write to it in another thread.

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