首页 文章

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

提问于
浏览
119

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

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

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

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

还有其他想法吗?谢谢 .

13 回答

  • 1

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

    看看FutureTaskExecutorService是否有现成的做这种事情的方法 .

    我强烈建议阅读Java Concurrency In Practice对任何对并发和多线程感兴趣的人 . 它显然集中在Java上,但是对于任何使用其他语言的人来说都有很多东西 .

  • 132

    我真的建议你在开始多线程的神奇世界之前先阅读像_2665632这样的教程 .

    还有很多好书(google为“Java中的并发编程”,“实践中的Java并发”) .

    To get to your answer:

    在必须等待 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 优选地是您需要从每个线程并发操作的对象,或者您可以为此目的创建单独的 Object (我不建议使方法本身同步):

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

    To further your understanding:
    还有其他(有时更好的)方法来完成上述工作,例如:与 CountdownLatches 等等 . 自Java 5以来, java.util.concurrent 包和子包中有很多漂亮的并发类 . 你真的需要在网上找到材料来了解并发性,或者获得一本好书 .

  • 6

    使用CountDownLatch,计数器为1 .

    CountDownLatch latch = new CountDownLatch(1);
    

    现在在app线程中 -

    latch.await();
    

    在db线程中,完成后,执行 -

    latch.countDown();
    
  • 2

    要求::

    等待下一个线程的执行,直到上一个完成 . 无论时间消耗如何,下一个线程都必须在前一个线程停止之前才能启动 . 它必须简单易用 .

    答案::

    @See 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并发 .

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

    像这样使用这个类:

    创建一个ThreadEvent:

    ThreadEvent resultsReady = new ThreadEvent();
    

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

    resultsReady.await();
    

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

    resultsReady.signal();
    

    EDIT:

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

    如果您100%确定在await()之后调用signal(),则只能使用此方法 . 这就是为什么你不能使用像Java这样的Java对象的一个重要原因 . Windows事件 .

    如果代码按此顺序运行:

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

    那么 thread 2 will wait forever . 这是因为Object.notify()只唤醒当前正在运行的线程之一 . 稍后等待的线程不会被唤醒 . 这与我期望事件工作的方式非常不同,在事件发出信号之前,a)等待或b)显式重置 .

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

  • 125

    尝试使用 java.util.concurrent 包中的CountDownLatch类,它提供更高级别的同步机制,远比任何低级别的东西都容易出错 .

  • 7

    你可以使用两个线程之间共享的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) {
    
    }
    

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

  • 4

    很多正确的答案,但没有一个简单的例子..这是一个简单而简单的方法如何使用 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
    
  • 22

    如果你想要快速和脏的东西,你可以在你的while循环中添加一个Thread.sleep()调用 . 如果数据库库是您无法更改的,那么实际上没有其他简单的解决方案 . 轮询数据库直到准备好等待时间不会导致性能下降 .

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

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

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

  • -1

    这适用于所有语言:

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

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

    我建议您在实现案例之前,先了解使用事件和事件处理程序的概念,以便更好地理解这个范例 .

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

    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
    
  • 2

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

  • 2

    以来

    您可以考虑其他替代方案:

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

    执行给定的任务,返回完成所有状态和结果的期货列表 .

    使用所有可用处理器作为目标并行级别创建工作窃取线程池 .

  • 8

    enter image description here

    这个想法可以适用吗?如果您使用CountdownLatches或Semaphores工作完美,但如果您正在寻找面试的最简单答案,我认为这可以适用 .

相关问题