您的位置:首页 > 编程语言 > Java开发

java线程池学习(三) —— ThreadPoolExecutor

2015-10-03 19:34 525 查看
上一篇文章中我们自己写了一个简单的线程池。

这一篇文章我们来了解一下java为我们提供的线程池实现——
ExecutorService接口

它位于jdk的java.util.concurrent包下。

JDK提供了这么两个类来实现这个接口:
ThreadPoolExecutor
ScheduledThreadPoolExecutor
我们这篇文章只介绍一下ThreadPoolExecutor类(ScheduledThreadPoolExecutor类类似,多加入了计划任务功能。)

我们首先看看怎么用ThreadPoolExecutor类初始化一个线程池:

//初始化一个线程池
//核心线程数
int  corePoolSize  = 5;
//最大线程数
int  maxPoolSize   = 10;
//空闲线程最大存活时间
long keepAliveTime = 5000;
//任务队列
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(5);

ExecutorService threadPoolExecutor =
new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.MILLISECONDS,
queue
);


这个类完全实现了一个类似于我们上一篇文章中实现的线程池,它包含以下几个属性:

1. corePoolSize 

 核心线程数:即使没有任何任务过来,线程池里面也会有保持的最基本线程数。

 2. maximumPoolSize

 最大线程数(即使任务特别多,线程池里的线程数也不会超过它)

 3. keepAliveTime
 空闲线程最大存活时间
 4. blockingQueue
 任务队列,用来存放待处理的任务。我们在这个系列的第一篇文章中就介绍过了它。
 可以选择以下几个阻塞队列。
ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。
SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue。
PriorityBlockingQueue:一个具有优先级得无限阻塞队列。  
那么整个类的结构就如下图所示:



要使用这个线程池的话,可以使用它提供给我们的如下方法:

execute(Runnable)
submit(Runnable)
submit(Callable)
invokeAny(...)
invokeAll(...)

execute()和submit()可以向这个线程池提交单个任务。他们的区别是:
使用execute提交任务,但是execute方法没有返回值,所以无法判断任务知否被线程池执行成功。

使用submit 方法来提交任务,它会返回一个future对象,那么我们可以通过这个future对象来判断任务是否执行成功

invokeAll可以直接把一个List类型的任务列表一次性的提交给线程池执行。
==================================================================================
那么接下来我们就用 ThreadPoolExecutor 来创建一个线程池,改写一下我们上一篇文章的例子:

public class ProblemCreater {

public static void main(String[] args) throws Exception {
//初始化线程池
//核心线程数
int  corePoolSize  = 5;
//最大线程数
int  maxPoolSize   = 10;
long keepAliveTime = 5000;

ExecutorService threadPoolExecutor =
new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(5)
);

//生成者不断产生任务
for(int i=1;i<10;i++){
//定义一个新的任务
Runnable task = new Runnable(){
public void run(){
Random random = new Random();
//随机一个数字模拟需要解决的时间
int randomTime = Math.abs(random.nextInt())%20;
try {
Thread.sleep(randomTime*1000);
System.out.println("任务完成,花费时间为:"+randomTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//将问题插入到线程池任务队列中
threadPoolExecutor.execute(task);
System.out.println("插入新的任务"+i);
}
}
}


我们再回头看看这个 ThreadPoolExecutor 的初始化,我们需要给它传递5个参数。核心线程数,最大线程数,线程存活时间,时间单位,还有阻塞队列的类型。

这个过程还是比较繁琐的。其实Java帮我们简化了这个过程,我们可以根据不同的情景,直接用一行代码创建一个合适的线程池。

实现这个功能的就是 java.util.concurrent.Executors类。我们在下一篇文章中再详细介绍这个类的用法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: