简体   繁体   中英

Multithreaded in java

I have a multithreaded program where I want the one of the thread to print the statement after all the thread finished.How can I do that? and How do I know that all the threads finished?

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");
    }
    }

That sounds like an ideal use case for 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 blocks until all the tasks in the supplied List are complete.

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.

You can use a CyclicBarrier with a barrier action ( documentation ).

Creates a new CyclicBarrier that will trip when the given number of parties (threads) are waiting upon it, and which will execute the given barrier action when the barrier is tripped, performed by the last thread entering the barrier.

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...");

      }

    }
  }
}

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");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM