简体   繁体   中英

Create threads in loop and wait until all threads finished in java

I am new to Java. I was studying multiple threads recently at school. I try to create a small program that can divide a task into smaller parts and run each part in a separate thread using a loop. The problem is that after the loop I need to sum the result and print it out but the print after the loop runs before the thread is finished.

All my classmates are adding sleep before printing the result but that does not work when the threads take too long.

Is there any way to wait for all the threads in the loop to finish first before running other code?

import java.util.Scanner;
import java.util.concurrent.ExecutorService;

import java.util.concurrent.TimeUnit;

public class threatGenerator {
    static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        Scanner sc = new Scanner(System.in);
        System.out.print("Input start: ");
        int start = Integer.parseInt(sc.nextLine());
        System.out.print("Input end: ");
        int end = Integer.parseInt(sc.nextLine());
        
        int threadNumber = (int)Math.ceil((end - start)/100.0) ;

        System.out.println("Running "+threadNumber+" threads.");
       
        for(int i = 0 ;i < threadNumber;i++){
            int temp = start+ 99;
            
            if(temp>end) temp = end;

            String name = String.valueOf(i);
            ThreadDemo thread = new ThreadDemo(name,start,temp);
            thread.start();
            
            start = temp+1;
        }
        Thread.sleep(10);
        System.out.println("\nSum of primes = "+count);
        sc.close();
    }

    public static void awaitTerminationAfterShutdown(ExecutorService threadPool) {
        threadPool.shutdown();
        try {
            if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
                threadPool.shutdownNow();
            }
        } catch (InterruptedException ex) {
            threadPool.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

class ThreadDemo extends Thread {
    private Thread t;
    private String threadName;
    private int start;
    private int end;
    private int num = 0;

    ThreadDemo( String name,int start,int end) {
        threadName= name;
        this.start = start;
        this.end = end;
    }
    public void run() {
        Prime p = new Prime();

        for(int i = start ; i<=end;i++){
            if(p.checkPrime(i)!=true){
                System.out.print("t"+threadName+"-"+i+" ");
                ++num;
            }
        }
        threatGenerator.count = threatGenerator.count + num;
    }
    
    public void setCount(int count){
        this.num=count;
    }

    public int getCount() {
        return this.num;
    }

    public void start () {
        // System.out.println("Starting " + threadName);
        if (t == null) {
            t = new Thread (this, threadName);
            t.start();
        }
    }
}

is there anyway to wait for all the thread in loop finished...?

If you change your program to keep all of the Thread references (eg, by adding them to an ArrayList<Thread> ,) then you can write a second loop that join() s all of the threads.

https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Thread.html#join()

t.join() waits (ie, does not return) until the thread t has terminated.

tl;dr

Using modern Java, define your task as a Callable that returns a result. Submit your task objects to an executor service. That service returns a Future object to track each task's completion status and result. Collect these Future objects.

Wait for your executor service to shut down. When done, loop your collected Future objects to retrieve the results of each task.

Executor service

In modern Java, we rarely need to address the Thread class directly. The executor service framework was created to handle the chore of juggling threads. See tutorial by Oracle .

Define your task as Runnable , or as a Callable when you expect a result to be returned.

When you submit a Callable to an executor service, you get back a Future object. Collect those Future objects, as they will contain the result of your Callable task object's work.

To wait for all the tasks to be done, or canceled, or failed, call ExecutorSerivce#shutdown and then ExecutorService#awaitTermination . That first one tells the service to shut down after all submitted tasks are finished. The second blocks the flow-of-control to wait until all the tasks are finished and the service has been shut down. The second one takes time-out parameters, to force a shutdown if need be.

When past those calls, loop your collection of Future objects. Ask each for its contents, the results of your tasks' work.

In the following example, we have a simple Callable that returns an Integer object with some number we imagine to be calculated but for the sake of this demo is simply generated randomly. We sleep for a random few seconds, to simulate lots of real work that takes a while.

    class IntegerProvider implements Callable < Integer >
    {
        @Override
        public Integer call ( ) throws Exception
        {
            System.out.println( "INFO - Starting `call` method. " + Instant.now() );
            // Pretend we have lots of work to do by putting this thread to sleep a few seconds.
            Duration duration = Duration.ofSeconds( ThreadLocalRandom.current().nextInt( 1 , 7 ) );
            Thread.sleep( duration.toMillis() );
            // Now pretend we did some work to calculate a number as a result.
            int i = ThreadLocalRandom.current().nextInt( 7 , 42 );
            Integer result = java.lang.Integer.valueOf( i );
            return result;
        }
    }

Then we have some code to start an executor service, and submit several of these IntegerProvider task objects. When done, we sum total of their results.

        ExecutorService executorService = Executors.newFixedThreadPool( 3 );
        int limit = 20;
        List < Future < Integer > > futures = new ArrayList <>( limit );
        for ( int i = 0 ; i < limit ; i++ )
        {
            IntegerProvider callableTask = new IntegerProvider();
            Future < Integer > future = executorService.submit( callableTask );
            futures.add( future );
        }

        // Wait for submitted tasks to be done/canceled/failed.
        executorService.shutdown();
        try { executorService.awaitTermination( 1 , TimeUnit.HOURS ); } catch ( InterruptedException e ) { e.printStackTrace(); }

        // At this point, all the submitted tasks are done. Process results.
        int total = 0;
        for ( Future < Integer > future : futures )
        {
            // Process each `future` object. Get the result of each task's calculation. Sum total.
            if ( future.isCancelled() )
            {
                System.out.println( "Oops, this future is canceled." );
            } else if ( future.isDone() )
            {
                try { total = ( total + future.get() ); }
                catch ( InterruptedException e ) { e.printStackTrace(); }
                catch ( ExecutionException e ) { e.printStackTrace(); }
            } else
            {
                System.out.println( "ERROR - Should never reach this point." );
            }
        }
        System.out.println( "total = " + total );

Here is a complete single class to demonstrate.

package work.basil.numbers;

import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class App
{
    public static void main ( String[] args )
    {
        App app = new App();
        app.demo();
    }

    private void demo ( )
    {
        System.out.println( "INFO - Starting `demo` method. " + Instant.now() );

        ExecutorService executorService = Executors.newFixedThreadPool( 3 );
        int limit = 20;
        List < Future < Integer > > futures = new ArrayList <>( limit );
        for ( int i = 0 ; i < limit ; i++ )
        {
            IntegerProvider callableTask = new IntegerProvider();
            Future < Integer > future = executorService.submit( callableTask );
            futures.add( future );
        }

        // Wait for submitted tasks to be done/canceled/failed.
        executorService.shutdown();
        try { executorService.awaitTermination( 1 , TimeUnit.HOURS ); } catch ( InterruptedException e ) { e.printStackTrace(); }

        // At this point, all the submitted tasks are done. Process results.
        int total = 0;
        for ( Future < Integer > future : futures )
        {
            // Process each `future` object. Get the result of each task's calculation. Sum total.
            if ( future.isCancelled() )
            {
                System.out.println( "Oops, this future is canceled." );
            } else if ( future.isDone() )
            {
                try { total = ( total + future.get() ); }
                catch ( InterruptedException e ) { e.printStackTrace(); }
                catch ( ExecutionException e ) { e.printStackTrace(); }
            } else
            {
                System.out.println( "ERROR - Should never reach this point." );
            }
        }
        System.out.println( "total = " + total );
        System.out.println( "INFO - Ending `demo` method. " + Instant.now() );
    }

    class IntegerProvider implements Callable < Integer >
    {
        @Override
        public Integer call ( ) throws Exception
        {
            System.out.println( "INFO - Starting `call` method. " + Instant.now() );
            // Pretend we have lots of work to do by putting this thread to sleep a few seconds.
            Duration duration = Duration.ofSeconds( ThreadLocalRandom.current().nextInt( 1 , 7 ) );
            Thread.sleep( duration.toMillis() );
            // Now pretend we did some work to calculate a number as a result.
            int i = ThreadLocalRandom.current().nextInt( 7 , 42 );
            Integer result = java.lang.Integer.valueOf( i );
            return result;
        }
    }
}

When run.

INFO - Starting `demo` method. 2021-03-28T22:47:21.717806Z
INFO - Starting `call` method. 2021-03-28T22:47:21.743966Z
INFO - Starting `call` method. 2021-03-28T22:47:21.744736Z
INFO - Starting `call` method. 2021-03-28T22:47:21.744890Z
INFO - Starting `call` method. 2021-03-28T22:47:22.745855Z
INFO - Starting `call` method. 2021-03-28T22:47:24.749539Z
INFO - Starting `call` method. 2021-03-28T22:47:24.749522Z
INFO - Starting `call` method. 2021-03-28T22:47:25.749902Z
INFO - Starting `call` method. 2021-03-28T22:47:26.749692Z
INFO - Starting `call` method. 2021-03-28T22:47:26.750092Z
INFO - Starting `call` method. 2021-03-28T22:47:27.755313Z
INFO - Starting `call` method. 2021-03-28T22:47:28.752061Z
INFO - Starting `call` method. 2021-03-28T22:47:28.752030Z
INFO - Starting `call` method. 2021-03-28T22:47:28.757104Z
INFO - Starting `call` method. 2021-03-28T22:47:29.761592Z
INFO - Starting `call` method. 2021-03-28T22:47:30.755099Z
INFO - Starting `call` method. 2021-03-28T22:47:30.755230Z
INFO - Starting `call` method. 2021-03-28T22:47:31.756692Z
INFO - Starting `call` method. 2021-03-28T22:47:32.758877Z
INFO - Starting `call` method. 2021-03-28T22:47:33.760871Z
INFO - Starting `call` method. 2021-03-28T22:47:35.764750Z
total = 519
INFO - Ending `demo` method. 2021-03-28T22:47:38.770971Z

**this one for my refrecnce... It is also supposed to do same work

  import java.util.ArrayList;

  import java.util.List;

  import java.util.concurrent.Callable;

  import java.util.concurrent.ExecutionException;

  import java.util.concurrent.ExecutorService;

  import java.util.concurrent.Executors;


  public class MyThread {
private final MyService myService;
private final ExecutorService executor;
private final List<String> responses = new ArrayList<>();

public MyThread(MyService myService) {
    this.myService = myService;
    this.executor = Executors.newFixedThreadPool(10);
}

public void run(List<Integer> elements) {
    for (Integer element : elements) {
        Callable<String> task = () -> myService.process(element);
        Future<String> future = executor.submit(task);
        try {
            String response = future.get();
            responses.add(response);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

}

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