等待几个线程中的一个

时间:2010-06-22 20:24:05

标签: java multithreading

我有一个java应用程序,其中主线程启动2个其他线程。 如果其中一个线程终止,则主线程可能会启动另一个线程,具体取决于终止线程的结果。

实施例: 主线程创建2个线程:A和B.线程A将加载图片,线程B将加载另一个图片。如果A终止并成功加载图片,将创建一个新的Thread C,它会执行其他一些操作等等。

我该怎么做?我不想在主线程中使用忙等待,并且如果两个线程中的一个已经完成则每100ms检查一次。 我想我不能使用线程池,因为活动线程的数量(在这种情况下是A和B)会有很大变化,而且它是创建新线程的主线程决定。

这是“忙碌等待”解决方案的粗略草图:

public class TestThreads {
 private class MyThread extends Thread {
  volatile boolean done = false;
  int steps;

  @Override
  public void run() {
   for (int i=0; i<steps; i++) {
    System.out.println(Thread.currentThread().getName() + ": " + i);
    try {
     Thread.sleep(1000);
    } catch (InterruptedException exc) {  }
   }
   done = true;
   synchronized (this) {
    notify();
   }
  }

  public void waitFor(long ms) {
   synchronized (this) {
    try {
     wait(ms);
    } catch (InterruptedException exc) {  }    
   }
  }
 }

 public void startTest() {
  MyThread a = new MyThread();
  a.steps = 6;
  a.start();

  MyThread b = new MyThread();
  b.steps = 3;
  b.start();

  while (true) {
   if (!a.done) {
    a.waitFor(100);
    if (a.done) {
     System.out.println("C will be started, because A is done.");
    }
   }

   if (!b.done) {
    b.waitFor(100);
    if (b.done) {
     System.out.println("C will be started, because B is done.");
    }
   }

   if (a.done && b.done) {
    break;
   }
  }
 }

 public static void main(String[] args) {
  TestThreads test = new TestThreads();
  test.startTest();
 }
}

5 个答案:

答案 0 :(得分:3)

这听起来像是使用ThreadPoolExecutor同时执行任务并使用ExecutorCompletionService包装它来收集结果的经典案例。

例如,假设 tasks 包含一组要并行执行的任务,每个任务在终止时返回一个String值,处理结果的代码在可用时可以是:

List<Callable<String>> tasks = ....;
Executor ex = Executors.newFixedThreadPool(10);
ExecutorCompletionService<String> ecs = new ExecutorCompletionService<String>(ex);
for (Callable<String> task : tasks) 
    ecs.submit(task);
for(int i = 0; i < tasks.size(); i++) {
    String result = ecs.take().get();
    //Do something with result
}

如果您将任务的标识作为返回值的一部分包含在内,那么您可以根据完成顺序做出决定。

答案 1 :(得分:2)

检查Semaphore

  

计数信号量。从概念上讲,信号量保持一组许可。如果需要,每个acquire()都会阻止,直到许可证可用,然后接受

因此,无论何时线程完成,它都会释放一个许可证,然后由主线程

获取

答案 2 :(得分:1)

您应该使用线程池。在线程池中,您有一定数量的线程,任务保存在队列中;只要线程可用,任务就从队列中取出并由该线程执行。

以下是有关线程池Sun tutorial的链接。

编辑:刚刚注意到您在答案中写道,您认为自己无法使用线程池。我不明白为什么会这样。如果您担心创建开销,可以将线程设置为按需创建,而不是一次创建,一旦创建了空闲线程,实际上不会伤害任何内容。

你还说这是主线程决定是否创建一个新线程,但它真的需要吗?我认为这可能会让你感到过于复杂。

答案 3 :(得分:0)

是否有理由直接控制线程执行而不是使用类似的东西 ExecutorService

@danben先到了那里,但我陷入了同样的陷阱陷阱。

答案 4 :(得分:0)

代码的很多复杂性是主线程试图等待两个不同的对象。没有什么说你不能在另一个对象上使用wait和notify,如果你的任务是(A或B)然后是C,下面的代码将起作用 - 等待一个被设置的引用来指示完成的第一个任务。

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

public class BiggieThreads
{
    private static class MyTask implements Runnable
    {
        final int steps;
        final AtomicReference<MyTask> shared;
        final String name;

        MyTask ( int steps, AtomicReference<MyTask> shared, String name )
        {
            this.shared = shared;
            this.steps = steps;
            this.name = name;
        }

        @Override
        public void run()
        {
            for ( int i = 1; i <= steps; i++ ) {
                System.out.println ( "Running: " + this  + " " + i + "/" + steps);
                try {
                    Thread.sleep ( 100 );
                } catch ( InterruptedException exc ) {  }
            }

            // notify if this is the first to complete
            if ( shared.compareAndSet ( null, this ) )
                synchronized ( shared ) {
                    shared.notify();
                }

            System.out.println ( "Completed: " + this );
        }

        @Override
        public String toString ()
        {
            return name;
        }
    }

    public void startTest() throws InterruptedException
    {
        final ExecutorService pool = Executors.newFixedThreadPool ( 3 );
        final AtomicReference<MyTask> shared = new AtomicReference<MyTask>();

        Random random = new Random();

        synchronized ( shared ) {
            // tasks launched while lock on shared held to prevent
            // them notifying before this thread waits
            pool.execute ( new MyTask ( random.nextInt ( 5 ) + 3, shared, "a" ) );
            pool.execute ( new MyTask ( random.nextInt ( 5 ) + 3, shared, "b" ) );

            shared.wait();
        }

        System.out.println ( "Reported: " + shared.get() );

        pool.shutdown();
    }

    public static void main ( String[] args ) throws InterruptedException
    {
        BiggieThreads test = new BiggieThreads ();
        test.startTest();
    }
}

我倾向于在生产中使用信号量来完成这项工作,因为虽然等待很简单,但在信号量中使用会给行为命名,所以下次读代码时可以解决这个问题。