0

我有一个多线程程序,我希望其中一个线程在所有线程完成后打印语句。我该怎么做?我怎么知道所有线程都完成了?

ExecutorService pool = Executors.newCachedThreadPool();
    for(int i = 0; i < myList.size(); ++i) {
            pool.execute (new ThreadProcessRunnable (args));
    }


    public class ThreadProcessRunnable implements Runnable {
           public void run() {


           System.out.println("last thread should execute this");
    }
    }
4

4 回答 4

3

这听起来像是一个理想的用例ExecutorService.invokeAll

ExecutorService pool = Executors.newCachedThreadPool();
List<Callable<Object>> tasks = new ArrayList<Callable<Object>>();
for(int i = 0; i < myList.size(); ++i) {
    tasks.add (Executors.callable(new ThreadProcessRunnable (args)));
}
List<Future<Object>> futures = pool.invokeAll(tasks);
System.out.println("All tasks finished");


public class ThreadProcessRunnable implements Runnable {
    public void run() {
        // do some stuff
    }
}

invokeAll阻塞,直到提供的所有任务List都完成。

If you absolutely must have the println inside one of the threads' run methods, then the simplest approach I can think of would be to keep some sort of counter in an AtomicInteger

public class ThreadProcessRunnable implements Runnable {
  private AtomicInteger taskCounter;

  public ThreadProcessRunnable(AtomicInteger counter) {
    this.taskCounter = counter;
  }

  public void run() {
    // do stuff
    if(taskCounter.decrementAndGet() == 0) {
      System.out.println("I am the last thread and I am about to finish");
    }
  }
}

// Main class
ExecutorService pool = Executors.newCachedThreadPool();
AtomicInteger taskCounter = new AtomicInteger(myList.size());
for(int i = 0; i < myList.size(); ++i) {
    pool.execute(new ThreadProcessRunnable(taskCounter));
}

The key thing that makes this work is that taskCounter.decrementAndGet is atomic - if the value of taskCounter is initially 2, for example, and two different threads call decrementAndGet at the same time then it is guaranteed that one thread will see the value 1 and the other thread will see the value 0, so exactly one thread will print the "about to finish" message. This is different from MadProgrammer's answer, which involves a race condition:

latch.countDown();
if(latch.getCount() == 0) { ... }

where it is possible to have thread 1 decrement the value (to 1), then thread 2 decrement it again (to 0), then both threads see the value 0 when they call getCount and both print the message.

于 2013-01-17T22:08:42.777 回答
2

您可以将 CyclicBarrier 与屏障操作一起使用(文档)。

创建一个新的 CyclicBarrier,当给定数量的参与方(线程)正在等待它时,它将触发,并且当障碍被触发时,它将执行给定的屏障动作,由最后一个进入屏障的线程执行。

于 2013-01-17T22:08:14.873 回答
2

This is a REALLY basic example/concept of using a CountDownLatch

public class TestCountDownLatch {

  private static CountDownLatch latch;

  public static void main(String[] args) {

    latch = new CountDownLatch(10);
    ExecutorService pool = Executors.newCachedThreadPool();
    for (int i = 0; i < 10; ++i) {
      pool.execute(new Worker(i));
    }
  }

  public static class Worker implements Runnable {

    private int number;

    public Worker(int number) {
      this.number = number;
    }

    @Override
    public void run() {

      try {
        System.out.println(number + " is sleeping...");
        Thread.sleep((long) (Math.round(Math.random() * 1000)));
      } catch (InterruptedException ex) {
      }

      System.out.println(number + " is Completed...");
      latch.countDown();

      if (latch.getCount() == 0) {

        System.out.println(number + " was last...");

      }

    }
  }
}

Simple Single Thread Test Case

public class TestCountDownLatch {

  private static CountDownLatch latch;

  public static void main(String[] args) {

    latch = new CountDownLatch(1);
    ExecutorService pool = Executors.newCachedThreadPool();
    for (int i = 0; i < 1; ++i) {
      pool.execute(new Worker(i));
    }
  }

  public static class Worker implements Runnable {

    private int number;

    public Worker(int number) {
      this.number = number;
    }

    @Override
    public void run() {

      try {
        System.out.println(number + " is sleeping...");
        Thread.sleep((long) (Math.round(Math.random() * 1000)));
      } catch (InterruptedException ex) {
      }

      System.out.println(number + " is Completed...");
      latch.countDown();

      if (latch.getCount() == 0) {

        System.out.println(number + " was last...");

      }

    }
  }
}
于 2013-01-17T23:56:13.567 回答
1

You can place it in the main thread. Call pool.await() to block the main thread until all threads in the pool have finished, then do the extra work. The code would look like this:

ExecutorService pool = Executors.newCachedThreadPool();
for(int i = 0; i < myList.size(); ++i) {
        pool.execute (new ThreadProcessRunnable (args));
}
pool.shutdown();
pool.awaitTermination();//blocks the main thread
System.out.println("last thread should execute this");
于 2013-01-17T22:09:06.427 回答