【Java多线程】ExecutorService与ExecutorCompletionService

ExecutorService与ExecutorCompletionService都是java.util.concurrent包的并发处理类,总的来说,ExecutorCompletionService是ExecutorService的功能增强版,ExecutorCompletionService以BlockingQueue<Future<V>>来存放已经完成的任务。

也就是说,优先完成的任务会优先存放在BlockingQueue<Future<V>>队列中,所以我们能及时的拿到最优先的处理结果。

让我们先看看ExecutorService的测试代码,共4个任务,我们刻意让第1个任务的执行时间最长,依次递减,代码如下:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestExecutorService 
{
	public static void main(String[] args) throws InterruptedException, ExecutionException 
	{
		ExecutorService es = Executors.newFixedThreadPool(4);

		//第一个任务
		Callable<Integer> task1 = new Callable<Integer>() {
			public Integer call() throws Exception 
			{
				//耗时最长, 4秒
				Thread.sleep(4000);
				return 1;
			}
		};

		//第二个任务
		Callable<Integer> task2 = new Callable<Integer>() {
			public Integer call() throws Exception 
			{
				Thread.sleep(3000);
				return 2;
			}
		};

		//第三个任务
		Callable<Integer> task3 = new Callable<Integer>() {
			public Integer call() throws Exception 
			{
				Thread.sleep(2000);
				return 3;
			}
		};

		//第四个任务
		Callable<Integer> task4 = new Callable<Integer>() {
			public Integer call() throws Exception 
			{
				Thread.sleep(1000);
				return 4;
			}
		};

		Future<Integer> result1 = es.submit(task1);
		Future<Integer> result2 = es.submit(task2);
		Future<Integer> result3 = es.submit(task3);
		Future<Integer> result4 = es.submit(task4);

		System.out.println("第1个任务等待中...");
		System.out.println("第1个任务完成:【" + result1.get() + "】");

		System.out.println("第2个任务等待中...");
		System.out.println("第2个任务完成:【" + result2.get() + "】");

		System.out.println("第3个任务等待中...");
		System.out.println("第3个任务完成:【" + result3.get() + "】");

		System.out.println("第4个任务等待中...");
		System.out.println("第4个任务完成:【" + result4.get() + "】");
	}
}

输出结果:

第1个任务等待中...
第1个任务完成:【1】
第2个任务等待中...
第2个任务完成:【2】
第3个任务等待中...
第3个任务完成:【3】
第4个任务等待中...
第4个任务完成:【4】

PS:Future.get()方法会造成阻塞,直到任务执行完毕为止。

运行代码可见,result1.get()阻塞了4秒后完成任务,输出结果,而紧随的result2.get(),result3.get(),result4.get()没有阻塞,立马输出结果。那是因为在result1.get()执行完毕时,其余3个任务早已执行完毕等待抓取结果了。所以,使用上述方法并不能得知哪个任务是最先返回结果的。

接下来,让我们看看ExecutorCompletionService的代码:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestExecutorComplectionService 
{
	public static void main(String[] args) throws InterruptedException, ExecutionException 
	{
		ExecutorService es = Executors.newFixedThreadPool(4);
		ExecutorCompletionService<Integer> ecs = new ExecutorCompletionService<Integer>(es);

		//第一个任务
		Callable<Integer> task1 = new Callable<Integer>() {
			public Integer call() throws Exception 
			{
				//耗时最长, 4秒
				Thread.sleep(4000);
				return 1;
			}
		};

		//第二个任务
		Callable<Integer> task2 = new Callable<Integer>() {
			public Integer call() throws Exception 
			{
				Thread.sleep(3000);
				return 2;
			}
		};

		//第三个任务
		Callable<Integer> task3 = new Callable<Integer>() {
			public Integer call() throws Exception 
			{
				Thread.sleep(2000);
				return 3;
			}
		};

		//第四个任务
		Callable<Integer> task4 = new Callable<Integer>() {
			public Integer call() throws Exception 
			{
				Thread.sleep(1000);
				return 4;
			}
		};

		ecs.submit(task1);
		ecs.submit(task2);
		ecs.submit(task3);
		ecs.submit(task4);

		for(int i = 0; i < 4; i++)
		{
			Future<Integer> result = ecs.take();
			System.out.println("输出结果:【" + result.get() + "】");
		}
	}
}

输出结果:

输出结果:【4】
输出结果:【3】
输出结果:【2】
输出结果:【1】

可见,第4个任务task4执行的时间是最短的,第1个输出结果。

下面让我们剖析一下ExecutorComplectionService的源码:

成员变量如下:

executor:ExecutorService类,任务并行执行器

completionQueue:就是保存执行结果的阻塞队列BlockingQueue

submit方法:底层依旧使用ExecutorService来并发执行任务,只不过是多了个功能【把执行完毕的任务放到complectionQueue队列中】

task方法:从complectionQueue队列中获取一个元素,如果没有元素,则阻塞,直到队列中有元素位置,这验证了我们之前的说法。

总结:总的来说,ExecutorComplectionService其实就是 ExecutorService 和 BlockingQueue的结合。

时间: 2024-10-17 08:42:00

【Java多线程】ExecutorService与ExecutorCompletionService的相关文章

java多线程(ExecutorService)

ExecutorService exec = null; List<IbeFlightInfo> ibeFlightInfo = new ArrayList<IbeFlightInfo>(); TransferVO[] b2gFlights = new TransferVO[]{}; try { exec = Executors.newFixedThreadPool(2); Callable IBEcall = new IBEGetFlightThread(request); Ca

【转】 Java 多线程之一

转自   Java 多线程 并发编程 一.多线程 1.操作系统有两个容易混淆的概念,进程和线程. 进程:一个计算机程序的运行实例,包含了需要执行的指令:有自己的独立地址空间,包含程序内容和数据:不同进程的地址空间是互相隔离的:进程拥有各种资源和状态信息,包括打开的文件.子进程和信号处理. 线程:表示程序的执行流程,是CPU调度执行的基本单位:线程有自己的程序计数器.寄存器.堆栈和帧.同一进程中的线程共用相同的地址空间,同时共享进进程锁拥有的内存和其他资源. 2.Java标准库提供了进程和线程相关

Java多线程并发技术

Java多线程并发技术 参考文献: http://blog.csdn.net/aboy123/article/details/38307539 http://blog.csdn.net/ghsau/article/category/1707779 http://www.iteye.com/topic/366591 JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式

java多线程使用学习笔记

初学Java多线程,后续继续改进 一,Callable Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其他线程执行的任务 Callable和Runnable的区别如下: 1.Callable定义的方法是call,而Runnable定义的方法是run. 2.Callable的call方法可以有返回值,而Runnable的run方法不能有返回值. 3.Callable的call方法可抛出异常,而Runnable的run方法不能抛出异常.

Java多线程与并发库高级应用 学习笔记 10-16课

Callable与Future的介绍 package Thread; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; im

Java多线程知识小抄集(三)

本文主要整理博主遇到的Java多线程的相关知识点,适合速记,故命名为"小抄集".本文没有特别重点,每一项针对一个多线程知识做一个概要性总结,也有一些会带一点例子,习题方便理解和记忆. 51. SimpleDateFormat非线程安全 当多个线程共享一个SimpleDateFormat实例的时候,就会出现难以预料的异常. 主要原因是parse()方法使用calendar来生成返回的Date实例,而每次parse之前,都会把calendar里的相关属性清除掉.问题是这个calendar是

Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例

概要 本章,我们对JUC包中的信号量Semaphore进行学习.内容包括:Semaphore简介Semaphore数据结构Semaphore源码分析(基于JDK1.7.0_40)Semaphore示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3534050.html Semaphore简介 Semaphore是一个计数信号量,它的本质是一个"共享锁". 信号量维护了一个信号量许可集.线程可以通过调用acquire()来获取信号量的许可

JAVA多线程和并发基础面试问答(转载)

原文链接:http://www.cnblogs.com/dolphin0520/p/3932934.html 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一.在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题. Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用.而线程是在进程中执行的一个任务.Java运行环境是一个包含

3种方式实现Java多线程

java中实现多线程的方法有两种:继承Thread类和实现runnable接口. 1.继承Thread类,重写父类run()方法 public class thread1 extends Thread { public void run() { for (int i = 0; i < 10000; i++) { System.out.println("我是线程"+this.getId()); } } public static void main(String[] args) {