`
xidajiancun
  • 浏览: 457705 次
文章分类
社区版块
存档分类
最新评论

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();
		}
		
		// 倒数器减1
		countDownLatch.countDown();
		
		System.out.println(this.getName() + "子线程结束");
	}
}

修改main:

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多线程–让主线程等待所有子线程执行完毕

     数据量很大百万条记录,因此考虑到要用多线程并发执行,在写的过程中又遇到问题,我想统计所有子进程执行完毕总共的耗时,在第一个子进程创建前记录当前时间用System.currentTimeMillis()在后一个子进程结束后...

    java大批量导入excel,多线程加分片处理的dome

    然后,创建一个固定数量的线程池,使用 CountDownLatch 控制主线程等待所有任务完成;最后,循环迭代分片区间,将分片任务提交到线程池中处理。在每个任务中,使用 ReadRowHolder 对象实现分片读取 Excel 数据,并...

    java线程池&子线程&事务&异步返回监听实战

    在java开发中,往往会遇到一个请求需要处理很多次数据库,想要分成多个线程异步处理,在此给出个一个试例完成上述需求,子事务的成败会控制主线程的事务,并利用了CompletableFuture实现异步监听,@Async注解实现了...

    ThreadPoolDemo:线程池相关

    那主线程和工作线程有什么区别的,其实本质上没太大区别,主线程因为是要跟用户直接打交道,实时交互性强,不能有其他的耗时操作阻塞其正常流程,不然出现丢帧卡顿的现象,因此 Android 是禁止在主线程中进行耗时...

    基于Java子线程中的异常处理方法(通用)

    下面小编就为大家带来一篇基于Java子线程中的异常处理方法(通用)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    Java进阶学习——Java多线程知识的理解

    Java多线程知识的理解0.前言1.从一道题目引入2.创建线程2.1.继承Thread类2.2.实现Runnable接口3.线程的生命周期4.线程安全4.1.为什么需要线程安全?4.2.如何实现线程安全?...为什么主线程早于子线

    spring boot内置jetty

    spring boot内置jetty开发,附带一些基础小功能,比如上传下载文件,走马灯,图片预览等等。

    使用Java异步编程实现一个简单的网络请求.txt

    在主线程中,我们通过调用response1.get()和response2.get()方法来获取异步计算的结果。由于这两个方法都是阻塞的(即等待异步计算完成后才会返回结果),因此我们需要等待这两个请求完成后才能继续执行后续代码。...

    thread count

    利用这种特性,可以让主线程等待子线程的结束。下面以一个模拟运动员比赛的例子加以说明。 import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent....

    java8源码-concurrency:java并发总结

    java8 源码 Java 并发多线程从简到全 参考: 目录: [TOC] 相关文档: kwseeker/netty Executors线程池.md ...CPU与线程的关系 ...由于不知道什么时候线程执行完毕并返回结果,主线程只能主动轮询查看线程

    spring-async-mdc:在具有 Spring 的异步支持的池线程上填充 MDC 的示例

    弹簧异步mdc 在具有 Spring 的异步支持的池线程上填充 MDC 的示例的建议解决方案

    《Android系统源代码情景分析》

    15.1 应用程序主线程消息循环模型 15.2 与界面无关的应用程序子线程消息循环模型 15.3 与界面相关的应用程序子线程消息循环模型 第16章 Android应用程序的安装和显示过程 16.1 应用程序的安装过程 16.2 ...

    Android开发艺术探索.任玉刚(带详细书签).pdf

    11.1 主线程和子线程 392 11.2 Android中的线程形态 392 11.2.1 AsyncTask 392 11.2.2 AsyncTask的工作原理 395 11.2.3 HandlerThread 402 11.2.4 IntentService 403 11.3 Android中的线程池 406 11.3.1 ...

    Android系统源代码情景分析-罗升阳-源码

    15.1 应用程序主线程消息循环模型 15.2 与界面无关的应用程序子线程消息循环模型 15.3 与界面相关的应用程序子线程消息循环模型 第16章 Android应用程序的安装和显示过程 16.1 应用程序的安装过程 16.2 应用...

    Android开发艺术探索

     11.1 主线程和子线程 / 392  11.2 Android中的线程形态 / 392  11.2.1 AsyncTask / 392  11.2.2 AsyncTask的工作原理 / 395  11.2.3 HandlerThread / 402  11.2.4 IntentService / 403  11.3 Android中的...

    JAVA程序设计教程

    Java程序.............................................................................................6 1.3.1 Java程序的结构 ...........................................................................

    精通ANDROID 3(中文版)1/2

    13.3.2 在工作线程与主线程之间通信  13.3.3 线程行为概述  13.4 处理程序示例驱动程序类  13.4.1 驱动程序活动文件  13.4.2 布局文件  13.4.3 菜单文件  13.4.4 描述文件  13.5 组件和进程寿命  ...

    精通Android 3 (中文版)2/2

    13.3.2 在工作线程与主线程之间通信  13.3.3 线程行为概述  13.4 处理程序示例驱动程序类  13.4.1 驱动程序活动文件  13.4.2 布局文件  13.4.3 菜单文件  13.4.4 描述文件  13.5 组件和进程寿命  ...

    android开发艺术探索高清完整版PDF

    / 375 10.2.2 消息队列的工作原理 / 380 10.2.3 Looper的工作原理 / 383 10.2.4 Handler的工作原理 / 385 10.3 主线程的消息循环 / 389 第11章 Android的线程和线程池 / 391 11.1 主线程和子线程 / 392 11.2 ...

Global site tag (gtag.js) - Google Analytics