【问题标题】:Create threads in loop and wait until all threads finished in java在循环中创建线程并等待所有线程在java中完成
【发布时间】:2021-06-24 18:56:09
【问题描述】:

我是 Java 新手。我最近在学校学习多个线程。我尝试创建一个小程序,可以将任务分成更小的部分,并使用循环在单独的线程中运行每个部分。问题是在循环之后我需要对结果求和并打印出来,但是循环之后的打印在线程完成之前运行。

我所有的同学都在打印结果之前添加了sleep,但是当线程花费太长时间时这不起作用。

有什么方法可以等待循环中的所有线程先完成,然后再运行其他代码?

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

【问题讨论】:

  • 更新静态变量threatGenerator.count时需要独占控制。否则,您可能无法得到正确的结果。
  • 看起来像是 java.util.concurrent.CountDownLatch 的工作。
  • 或者,您可以考虑the following answer
  • 请输入正确的大小写和英文。这个网站更像维基百科,而不是一个休闲聊天室。
  • 将对所有创建和启动的线程的引用存储到一个列表中,然后在每个线程上调用 .join()。 .join() 方法是专为像你这样的情况设计的

标签: java multithreading loops wait notify


【解决方案1】:

是否要等待循环中的所有线程完成...?

如果您更改您的程序以保留所有 Thread 引用(例如,通过将它们添加到 ArrayList&lt;Thread&gt;,)然后您可以编写第二个循环 join()s 所有线程。

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

t.join() 等待(即不返回)直到线程 t 终止。

【讨论】:

    【解决方案2】:

    tl;博士

    使用现代 Java,将您的任务定义为返回结果的 Callable。将您的任务对象提交给执行器服务。该服务返回一个Future 对象来跟踪每个任务的完成状态和结果。收集这些Future 对象。

    等待您的执行程序服务关闭。完成后,循环您收集的 Future 对象以检索每个任务的结果。

    执行者服务

    在现代 Java 中,我们很少需要直接寻址 Thread 类。创建执行器服务框架是为了处理杂耍线程的杂务。见tutorial by Oracle

    将您的任务定义为Runnable,或者当您期望返回结果时定义为Callable

    当您将Callable 提交给执行器服务时,您会返回一个Future 对象。收集那些Future 对象,因为它们将包含您的Callable 任务对象的工作结果。

    要等待所有任务完成、取消或失败,请调用ExecutorSerivce#shutdown,然后调用ExecutorService#awaitTermination。第一个告诉服务在所有提交的任务完成后关闭。第二个阻塞控制流以等待所有任务完成并且服务已关闭。第二个采用超时参数,如果需要强制关闭。

    通过这些调用后,循环您的 Future 对象集合。询问每个人的内容,即你的任务工作的结果。

    在下面的示例中,我们有一个简单的Callable,它返回一个Integer 对象,其中包含我们想象要计算的一些数字,但为了这个演示,它只是随机生成的。我们随机睡几秒钟,以模拟大量需要一段时间的实际工作。

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

    然后我们有一些代码来启动一个执行器服务,并提交其中几个IntegerProvider 任务对象。完成后,我们将它们的结果相加。

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

    这是一个完整的单类来演示。

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

    运行时。

    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
    

    【讨论】:

    • 最初的问题似乎不是关于多线程编程中的最佳实践,而是具体如何等待java线程完成。我也确信人们应该首先学习/理解 java 线程是如何工作的(包括 JMM),然后他们才能学习/使用构建在线程之上的任何类型的包装器/框架,以使其更加......“现代” :)
    猜你喜欢
    • 2011-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多