`

Java主线程等待子线程、线程池

    博客分类:
  • java
阅读更多
public class TestThread extends Thread  
{  
    public void run()  
    {  
        System.out.println(this.getName() + "子线程开始");  
        try  
        {  
            // 子线程休眠五秒  
            Thread.sleep(5000);  
        }  
        catch (InterruptedException e)  
        {  
            e.printStackTrace();  
        }  
        System.out.println(this.getName() + "子线程结束");  
    }  
}  

 

首先是一个线程,它执行完成需要5秒。

1、主线程等待一个子线程

public class Main  
{  
    public static void main(String[] args)  
    {  
        long start = System.currentTimeMillis();  
          
        Thread thread = new TestThread();  
        thread.start();  
          
        long end = System.currentTimeMillis();  
        System.out.println("子线程执行时长:" + (end - start));  
    }  
}  

 

在主线程中,需要等待子线程执行完成。但是执行上面的main发现并不是想要的结果:

子线程执行时长:0
Thread-0子线程开始
Thread-0子线程结束

很明显主线程和子线程是并发执行的,主线程并没有等待。

对于只有一个子线程,如果主线程需要等待子线程执行完成,再继续向下执行,可以使用Thread的join()方法。join()方法会阻塞主线程继续向下执行。

public class Main  
{  
    public static void main(String[] args)  
    {  
        long start = System.currentTimeMillis();  
          
        Thread thread = new TestThread();  
        thread.start();  
          
        try  
        {  
            thread.join();  
        }  
        catch (InterruptedException e)  
        {  
            e.printStackTrace();  
        }  
          
        long end = System.currentTimeMillis();  
        System.out.println("子线程执行时长:" + (end - start));  
    }  
}  

 

执行结果:

Thread-0子线程开始
Thread-0子线程结束
子线程执行时长:5000

注意:join()要在start()方法之后调用。

2、主线程等待多个子线程

比如主线程需要等待5个子线程。这5个线程之间是并发执行。

public class Main  
{  
    public static void main(String[] args)  
    {  
        long start = System.currentTimeMillis();  
          
        for(int i = 0; i < 5; i++)  
        {  
            Thread thread = new TestThread();  
            thread.start();  
              
            try  
            {  
                thread.join();  
            }  
            catch (InterruptedException e)  
            {  
                e.printStackTrace();  
            }  
        }  
          
        long end = System.currentTimeMillis();  
        System.out.println("子线程执行时长:" + (end - start));  
    }  
}  

 

在上面的代码套上一个for循环,执行结果:

Thread-0子线程开始
Thread-0子线程结束
Thread-1子线程开始
Thread-1子线程结束
Thread-2子线程开始
Thread-2子线程结束
Thread-3子线程开始
Thread-3子线程结束
Thread-4子线程开始
Thread-4子线程结束
子线程执行时长:25000

由于thread.join()阻塞了主线程继续执行,导致for循环一次就需要等待一个子线程执行完成,而下一个子线程不能立即start(),5个子线程不能并发。

要想子线程之间能并发执行,那么需要在所有子线程start()后,在执行所有子线程的join()方法。

public class Main 

    public static void main(String[] args) 
    { 
        long start = System.currentTimeMillis(); 
         
        List<Thread> list = new ArrayList<Thread>(); 
        for(int i = 0; i < 5; i++) 
        { 
            Thread thread = new TestThread(); 
            thread.start(); 
            list.add(thread); 
        } 
         
        try 
        { 
            for(Thread thread : list) 
            { 
                thread.join(); 
            } 
        } 
        catch (InterruptedException e) 
        { 
            e.printStackTrace(); 
        } 
         
        long end = System.currentTimeMillis(); 
        System.out.println("子线程执行时长:" + (end - start)); 
    } 

执行结果:

Thread-0子线程开始
Thread-3子线程开始
Thread-1子线程开始
Thread-2子线程开始
Thread-4子线程开始
Thread-3子线程结束
Thread-0子线程结束
Thread-2子线程结束
Thread-1子线程结束
Thread-4子线程结束
子线程执行时长:5000

3、主线程等待多个子线程(CountDownLatch实现)

CountDownLatch是java.util.concurrent中的一个同步辅助类,可以把它看做一个倒数计数器,就像神舟十号发射时倒数:10,9,8,7….2,1,0,走你。初始化时先设置一个倒数计数初始值,每调用一次countDown()方法,倒数值减一,await()方法会阻塞当前进程,直到倒数至0。

同样还是主线程等待5个并发的子线程。修改上面的代码,在主线程中,创建一个初始值为5的CountDownLatch,并传给每个子线程,在每个子线程最后调用countDown()方法对倒数器减1,当5个子线程等执行完成,那么CountDownLatch也就倒数完成,主线程调用await()方法等待5个子线程执行完成。

修改MyThread接收传入的CountDownLatch:

public class TestThread extends Thread    
{    
    private CountDownLatch countDownLatch;    
            
    public TestThread(CountDownLatch countDownLatch)    
    {    
        this.countDownLatch = countDownLatch;    
    }    
  
    public void run()    
    {    
        System.out.println(this.getName() + "子线程开始");    
        try    
        {    
            // 子线程休眠五秒    
            Thread.sleep(5000);    
        }    
        catch (InterruptedException e)    
        {    
            e.printStackTrace();    
        }  
  
        System.out.println(this.getName() + "子线程结束");  
            
        // 倒数器减1  
        countDownLatch.countDown();  
    }  
}  

 

public class Main  
{  
    public static void main(String[] args)  
    {  
        long start = System.currentTimeMillis();  
          
        // 创建一个初始值为5的倒数计数器  
        CountDownLatch countDownLatch = new CountDownLatch(5);  
        for(int i = 0; i < 5; i++)  
        {  
            Thread thread = new TestThread(countDownLatch);  
            thread.start();  
        }  
          
        try  
        {  
            // 阻塞当前线程,直到倒数计数器倒数到0  
            countDownLatch.await();  
        }  
        catch (InterruptedException e)  
        {  
            e.printStackTrace();  
        }  
          
        long end = System.currentTimeMillis();  
        System.out.println("子线程执行时长:" + (end - start));  
    }  
}  

 执行结果:

 

Thread-0子线程开始
Thread-2子线程开始
Thread-1子线程开始
Thread-3子线程开始
Thread-4子线程开始
Thread-2子线程结束
Thread-4子线程结束
Thread-1子线程结束
Thread-0子线程结束
Thread-3子线程结束
子线程执行时长:5000

注意:如果子线程中会有异常,那么countDownLatch.countDown()应该写在finally里面,这样才能保证异常后也能对计数器减1,不会让主线程永远等待。

另外,await()方法还有一个实用的重载方法:public booleanawait(long timeout, TimeUnit unit),设置超时时间。

例如上面的代码,想要设置超时时间10秒,到了10秒无论是否倒数完成到0,都会不再阻塞主线程。返回值是boolean类型,如果是超时返回false,如果计数到达0没有超时返回true。

// 设置超时时间为10秒  
boolean timeoutFlag = countDownLatch.await(10,TimeUnit.SECONDS);  
if(timeoutFlag)  
{  
    System.out.println("所有子线程执行完成");  
}  
else  
{  
    System.out.println("超时");  
} 

 4、主线程等待线程池

 

Java线程池java.util.concurrent.ExecutorService是很好用的多线程管理方式。ExecutorService的一个方法boolean awaitTermination(long timeout, TimeUnit unit),即阻塞主线程,等待线程池的所有线程执行完成,用法和上面所说的CountDownLatch的public boolean await(long timeout,TimeUnit unit)类似,参数设置一个超时时间,返回值是boolean类型,如果超时返回false,如果线程池中的线程全部执行完成,返回true。

由于ExecutorService没有类似CountDownLatch的无参数的await()方法,只能通过awaitTermination来实现主线程等待线程池。

public class Main  
{  
    public static void main(String[] args)  
    {  
        long start = System.currentTimeMillis();  
          
        // 创建一个同时允许两个线程并发执行的线程池  
        ExecutorService executor = Executors.newFixedThreadPool(2);  
        for(int i = 0; i < 5; i++)  
        {  
            Thread thread = new TestThread();  
            executor.execute(thread);  
        }  
        executor.shutdown();  
          
        try  
        {  
            // awaitTermination返回false即超时会继续循环,返回true即线程池中的线程执行完成主线程跳出循环往下执行,每隔10秒循环一次  
            while (!executor.awaitTermination(10, TimeUnit.SECONDS));  
        }  
        catch (InterruptedException e)  
        {  
            e.printStackTrace();  
        }  
          
        long end = System.currentTimeMillis();  
        System.out.println("子线程执行时长:" + (end - start));  
    }  
}  

 执行结果:

 

Thread-0子线程开始
Thread-1子线程开始
Thread-0子线程结束
Thread-2子线程开始
Thread-1子线程结束
Thread-3子线程开始
Thread-2子线程结束
Thread-4子线程开始
Thread-3子线程结束
Thread-4子线程结束
子线程执行时长:15000

另外,while(!executor.isTerminated())也可以替代上面的while (!executor.awaitTermination(10,TimeUnit.SECONDS)),isTerminated()是用来判断线程池是否执行完成。但是二者比较我认为还是awaitTermination更好,它有一个超时时间可以控制每隔多久循环一次,而不是一直在循环来消耗性能。

 

作者:叉叉哥   转载请注明出处:http://blog.csdn.net/xiao__gui/article/details/9213413

分享到:
评论

相关推荐

    JAVA主线程等待子线程执行完毕再执行[参照].pdf

    JAVA 主线程等待子线程执行完毕再执行 JAVA 中的线程控制是非常重要的一部分,而在实际开发中,我们经常会遇到需要主线程等待子线程执行完毕再执行的情况。这种情况下,我们可以使用两种方式来实现:主动式和被动式...

    Java简单实现“主线程等待所有子线程完成再继续”

    总的来说,Java提供了丰富的多线程同步机制,可以根据实际需求选择合适的方法来实现“主线程等待所有子线程完成再继续”的功能。在并发编程中,理解并灵活运用这些工具对于提高程序效率和避免死锁等问题至关重要。

    主线程等待子多线程(无结果返回)执行完成再继续执行

    "主线程等待子多线程(无结果返回)执行完成再继续执行"这个主题就涉及到如何在Java、C#、Python等编程语言中实现这种同步机制。下面将详细讨论这个知识点。 **1. Java中的`Thread.join()`方法** 在Java中,主线程...

    子线程任务发生异常,主线程事务如何回滚

    在 Java 中,存在两种线程模型:主线程和子线程。主线程是程序的入口点,而子线程是由主线程创建的辅助线程。子线程的运行结果可以通过阻塞的方式来获取。在 Java 中,我们可以使用 Callable 或 Runnable 接口来实现...

    子线程更新主线程数据

    然而,由于线程之间的数据同步问题,直接在子线程修改主线程的数据可能会导致数据不一致或程序崩溃。本文将深入探讨如何安全、有效地在子线程中更新主线程的数据。 首先,理解线程安全。线程安全是指当多个线程访问...

    Java多线程--等待所有子线程执行完的五种方法.docx

    尽管不推荐,但可以通过`Thread.sleep()`方法让主线程休眠一段时间来等待子线程。这种方法的问题在于,睡眠时间必须预估,且不准确,可能导致主线程提前唤醒或过度等待。 ### 方法二:`Thread.join()`方法 `Thread....

    Java多线程–让主线程等待所有子线程执行完毕

    在主线程中,使用一个循环等待所有子线程调用`join()`,这样可以确保所有子线程都完成后再继续主线程。 2. 使用`CountDownLatch`:Java并发包`java.util.concurrent`中的`CountDownLatch`类可以用来同步多个线程。...

    Java、Android多线程、线程池Demo

    在Java和Android开发中,多线程和线程池是两个关键的概念,它们对于提高程序的执行效率和优化系统资源的使用至关重要。本教程将深入探讨这两个主题,并通过一个名为"ThreadPoolDemo"的示例代码来阐述其核心原理和...

    epoll 多线程 线程池

    多线程和线程池则常用于后台任务处理、计算密集型任务或者IO密集型任务的异步执行,确保主线程不会因为等待而阻塞。 在一天的学习中,你可以先从理论入手,理解这三个概念的基本原理和使用场景。然后,通过阅读相关...

    C#Winform异步多线程和线程池集成的用法

    异步方法允许主线程在等待某个操作完成时继续执行其他任务,提高程序性能。例如,使用`Task.Run()`可以在后台运行方法: ```csharp private async void Button_Click(object sender, EventArgs e) { await Task....

    java多线程、并发及线程池介绍收藏的几篇文档

    - 异常处理:线程中抛出的异常不会影响主线程,需通过`Thread.UncaughtExceptionHandler`设置未捕获异常处理器。 6. **使用Java实现多线程服务器程序** - 对于服务器应用,通常使用多线程来处理客户端请求,每个...

    java多线程代码案例(创建线程,主线程,线程优先级,线程组,线程同步,线程间的通信)

    本文将深入探讨Java多线程中的关键知识点,包括创建线程、主线程、线程优先级、线程组、线程同步以及线程间的通信。 1. **创建线程** 在Java中,可以通过两种方式创建线程:继承`Thread`类或实现`Runnable`接口。...

    单线程 多线程 线程池 计算随机数的一价和

    最后,主线程收集所有工作线程的结果,将它们相加得到最终的一价和。为了确保计算的正确性,我们需要确保线程间的通信是安全的,例如使用`java.util.concurrent`包下的`ExecutorService`和`Future`接口,或者其他...

    线程,线程池与Handler的用法

    在Android开发中,线程和线程池是处理异步任务的重要工具,而Handler则是用于在主线程和子线程之间通信的关键组件。本篇文章将深入探讨这些概念,以及它们在实际应用中的用法。 首先,让我们理解什么是线程。线程是...

    java多线程查询数据库

    通常,我们需要确保线程池的`shutdownNow()`方法可以在遇到错误时停止所有任务,并在主线程中处理异常。 ### 8. 性能优化与调优 在实际应用中,我们还需要关注查询性能,比如使用预编译的SQL(PreparedStatement)...

    Android两个子线程之间的通信

    然而,当这些子线程需要与主线程交互,例如更新UI或共享数据时,就需要进行线程间通信。 Android提供了多种实现子线程间通信的方式: 1. **Handler-Message机制**:这是最常用的一种方式,通过创建Handler对象,一...

    java使用CountDownLatch等待多线程全部执行完成

    它的应用场景非常广泛,例如在处理大量数据时,可以使用多线程的方式处理,然后在主线程等待所有子线程处理完成。 CountDownLatch 的构造函数接收一个 int 类型的参数作为计数器,如果你想等待 N 个点完成,这里就...

    Java多线程实现异步调用实例

    `Thread.join()`可以让主线程等待子线程完成后再继续执行。 “异步”标签则涉及到了程序设计中的非阻塞特性,有助于提升系统的并发能力和响应性。Java 8引入的CompletableFuture提供了一种更高级的异步编程模型,...

    Android线程,线程池,AsyncTask,HandlerThread和IntentService的用法

    它内部包含一个Looper,可以在子线程中创建Handler,这样就能在子线程中接收和处理来自主线程的消息。HandlerThread适合长时间运行且需要与主线程通信的后台任务。 5. **IntentService**: IntentService是一个单...

    Java多线程的总结

    理解这些状态有助于我们更好地控制线程的行为,例如,通过调用sleep()方法可以使线程进入阻塞状态,join()方法可以使主线程等待子线程完成。 三、线程同步与互斥 在多线程环境下,数据共享可能会引发数据不一致的...

Global site tag (gtag.js) - Google Analytics