等待线程完成后再继续进行

10

当用户启动我的Android应用程序时,我会启动2个线程在后台进行一些处理。thread_1在客户端上进行一些计算,而thread_2从服务器获取一些数据。这一切都很好的工作。没有任何线程修改UI。我有两个后续问题。

new Thread(new Runnable(){
    @Override
    public void run(){
        MyClass.someStaticVariable = doSomeCalculations();
    }
}).start();
  1. 如何最佳实践地从线程的run()方法中检索数据?我目前使用静态变量,并将相关计算数据/获取的数据分配给它。或者建议使用Handler类从线程中获取数据吗?我想象只有在希望更新UI时才会使用处理程序。

while(true)
{
    if (!thread1.isAlive() && !thread2.isAlive())  
    {
        startActivity(intent)
    }
}
  • 在我能够通过 Intent 传递两个线程的数据之前,我需要等待这两个线程都完成。我该如何实现?我可以使用上面展示的代码来实现,但那似乎是不对的。


  • 1
    对于1 - 使用一个共享对象,通过Runnable的构造函数传递。对于2 - 使用Thread.join()。 - Mihai
    我认为你不能将一个变量/共享对象传递给可运行对象的构造函数! - Raunak
    1. 如建议,使用Thread.join()等待其完成;
    2. 您可以传递“final”引用,或通过MyClass.this.doCalc()调用封闭实例。
    - Michael M.
    如果他使用了Thread.join(),应用程序将被阻塞,直到线程完成其工作。 - Houcine
    是的,我的错。我忘了在Java中匿名类不能有带参数的构造函数。不过你可以把Runnable作为一个嵌套类来实现。 - Mihai
    4个回答

    12
    你可以使用Future,它会在获取数据之前进行阻塞:http://developer.android.com/reference/java/util/concurrent/Future.html 另一种方法是将CountDownLatch传递到线程中,并在退出run方法时调用countDown():http://developer.android.com/reference/java/util/concurrent/CountDownLatch.html
    final CountDownLatch latch = new CountDownLatch(2);
    new Thread(new Runnable(){
      @Override
      public void run(){
        // Do something
        latch.countDown()
      }
    }).start();
    new Thread(new Runnable(){
      @Override
      public void run(){
        // Do something
        latch.countDown()
      }
    }).start();
    
    latch.await()
    startActivity(intent)
    

    这似乎是在Android中等待两个线程完成的干净方法...尽管CountDownLatch似乎没有获取计算数据的方法... Future可以做到这一点...感谢您的输入。 - Raunak

    2

    在常规的Java应用程序中(Android应该也是一样),使用callable / Future / ExecutorService是最干净的方法。

    ExecutorService executor = Executors.newFixedThreadPool(2);
    Future<Integer> firstThreadResult = executor.submit(new Callable<Integer>() {
       Integer call() {
       }
    });
    
    Future<Integer> secondThreadResult = executor.submit(new Callable<Integer>() {
       Integer call() {
       }
    });
    
    executor.shutdown();
    executor.awaitTermination(Integer.MAX_VALUE,TimeUnit.SECONDS); // or specify smaller timeout
    // after this you can extract the two results
    firstThreadResult.get();
    secondThreadResult.get();
    

    更详细的示例请参见此处

    是的,我同意。这是在标准Java应用程序中的做法。我在思考这是否是在Android上最有效的方法。 - Raunak
    从效率上来看,它应该与您的方法相同——启动两个线程并使用join和共享数据等待它们完成。Callable和Futures使数据共享更加清洁,而不需要使用静态/实例变量。 - gkamal

    1
    1. 你可以使用共享对象(通过指向它的静态字段或任何其他方式),但必须注意两件事。首先,由两个线程访问的对象存在同步问题。使用不可变对象来缓解这个问题。其次,如何通知另一个线程新的共享数据已经可用 - 这取决于你的其他线程正在做什么。

    2. 设置一个公共标志,两个线程都检查或在完成时设置该标志。这样线程就可以在自己之前检查其他标志是否已完成。


    AsyncTask 内部使用 Handler 来在 UI 线程上进行回调以更新 UI。 - Kevin
    你认为使用自己的监听器如何?这是一种在你的线程上监听的监听器,当它完成时,你就可以执行想要执行的操作! - Houcine
    你只能在UI线程中更新UI。UI线程不会一直运行你的代码,实际上它主要运行你的代码来调用监听器。因此,你不能占用UI线程等待另一个线程。只需使用AsyncTask处理所有这些问题。 - Peter Knego
    这取决于上下文,你可以轻松地覆盖runOnUIThread方法,告诉它这段代码将在UIThread上执行。第二点:我同意你关于在启动一些耗时操作后有大量UI更新时使用AsynTask的观点。 - Houcine
    是的,这就是我的意思,像这样:this.runIOnUIThread(new Runnable(){... - Houcine
    显示剩余5条评论

    0

    针对问题1:

    从线程的run()方法中检索数据的最佳实践是什么?

    除了使用Future,您还可以从线程的run()方法中使用回调机制。在这个run()方法中,将值传递给调用者对象或在一个对象中设置相关值。

    使用Java中的Runnable实现回调

    针对问题2:

    我需要等待两个线程都完成后才能通过Intent传递两个线程的数据

    除了基本的join() API外,您可以通过多种方式实现它。

    1.ExecutorService invokeAll() API

    执行给定的任务,在所有任务完成时返回一个持有它们状态和结果的Future列表。

    2.CountDownLatch

    一个同步辅助工具,允许一个或多个线程等待其他线程正在执行的一组操作完成。
    3. ForkJoinPoolExecutors 中的 newWorkStealingPool() 请参考相关 SE 问题:

    如何在 Java 中等待所有线程完成工作


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接