【问题标题】:Waiting on threads等待线程
【发布时间】:2011-02-04 13:25:32
【问题描述】:

我有一个包含以下 (Java) 代码的方法:

doSomeThings();
doSomeOtherThings();

doSomeThings() 创建一些线程,每个线程只运行有限的时间。问题是我不希望在doSomeThings() 启动的所有线程完成之前调用doSomeOtherThings()。 (另外doSomeThings() 将调用可能启动新线程的方法等等。我不想在所有这些线程都完成之前执行doSomeOtherThings()。)

这是因为doSomeThings(),除其他外,会将myObject 设置为null,而doSomeOtherThings() 调用myObject.myMethod(),我不希望myObject 成为null

是否有一些标准的方式来做这种事情(在 Java 中)?

【问题讨论】:

    标签: java multithreading wait


    【解决方案1】:

    您可能想看看java.util.concurrent 包。特别是,您可以考虑使用CountDownLatch,如

    package de.grimm.game.ui;
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Main {
    
    public static void main(String[] args) 
    throws Exception {
    
        final ExecutorService executor = Executors.newFixedThreadPool(5);
        final CountDownLatch latch = new CountDownLatch(3);
    
        for( int k = 0; k < 3; ++k ) {
    
            executor.submit(new Runnable() {
                public void run() {
                    // ... lengthy computation...
                    latch.countDown();
                }
            });
        }
    
        latch.await();
    
        // ... reached only after all threads spawned have
        // finished and acknowledged so by counting down the
        // latch.
    
        System.out.println("Done");
    }
    }
    

    显然,这种技术只有在您事先知道分叉线程的数量时才有效,因为您需要使用该数字初始化锁存器。

    另一种方法是使用条件变量,例如:

    boolean done = false;
    
    void functionRunInThreadA() {
    
        synchronized( commonLock ) {
    
            while( !done ) commonLock.wait();
        }
    
        // Here it is safe to set the variable to null
    }
    
    void functionRunInThreadB() {
    
        // Do something...
    
        synchronized( commonLock ) {
            done = true;
            commonLock.notifyAll();
        }
    }
    

    您可能需要添加异常处理 (InteruptedException) 等。

    【讨论】:

    • (+1) CountdownLatch 非常方便
    【解决方案2】:

    看看Thread.join()方法。

    我不清楚您的确切实现,但似乎 doSomeThings() 应该在返回之前等待子线程。

    在doSomeThings()方法内部,通过调用Thread.join()方法等待线程。

    当您创建一个线程并调用该线程的 join() 方法时,调用线程会一直等待,直到该线程对象终止。

    例子:

    // Create an instance of my custom thread class
    MyThread myThread = new MyThread();
    // Tell the custom thread object to run
    myThread.start();
    // Wait for the custom thread object to finish
    myThread.join();
    

    【讨论】:

    • -1 调用 myThread.run 不会启动新线程。 myThread.join() 在 myThread.run() 完成时被调用的事实只是因为程序顺序而不是出于任何异步原因
    • @Donal,哎呀。谢谢。我更新了答案。 @John,对不起,这是一个错字。我习惯从 Runnable 继承而不是 Thread。
    【解决方案3】:

    您正在寻找的是 executorservice 并使用期货 :)

    http://java.sun.com/docs/books/tutorial/essential/concurrency/exinter.html

    所以基本上收集您提交给执行器服务的所有可运行文件的期货。循环所有期货并调用 get() 方法。这些将在相应的 runnable 完成后返回。

    【讨论】:

      【解决方案4】:

      另一个有用的更强大的Synchronization BarrierCyclicBarrier,您可以使用它来执行与CountdownLatch 类似的功能。它的工作方式类似于 CountdownLatch,您必须知道正在使用多少方(线程),但它允许您重用屏障,就像每次都创建一个新的 CountdownLatch 实例一样。

      我确实喜欢 momania 的建议,即使用 ExecutorService、收集期货并在所有这些期货上调用 get 直到它们完成。

      【讨论】:

        【解决方案5】:

        另一个选择是让你的主线程休眠,并让它每隔一段时间检查一下其他线程是否已经完成。不过,我更喜欢 Dirk 和 Marcus Adams 的回答——为了完整起见,我把它放在这里。

        【讨论】:

          【解决方案6】:

          取决于您在此处尝试执行的操作。您主要关心的是能够动态确定doSomeThings() 内部调用的连续方法产生的各种线程,然后能够等到它们完成后再调用doSomeOtherThings()?或者有可能知道在编译时产生的线程?在后一种情况下,有多种解决方案,但基本上都涉及在所有这些线程上从它们创建的位置调用Thread.join() 方法。

          如果确实是前者,那么你最好使用ThreadGroup 及其enumerate() 方法。如果您已将新线程正确添加到 ThreadGroup,这将为您提供由 doSomeThings() 生成的所有线程的数组。然后,您可以遍历返回数组中的所有线程引用,并在调用 doSomeOtherThings() 之前在主线程上调用 join()

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2023-03-11
            • 2011-08-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多