一、线程池的优点
关于线程池的使用优点网络上介绍的有很多,可以归结为以下几点:
1.减少在创建和销毁线程上所花的时间及系统资源的开销。
2.提高线程的可管理性,对线程进行统一的分配、调优和监控,从而也提高相应速度。
二、线程池的UML图和使用方式
线程池的UML图如下,使用线程池的方式总共有三种,下面将对这三种方式进行分析和说明。
2.1 第二种方式
由于第一种方式是通过用第二种方式的构造函数创建的,因此在介绍第一种方式前,先对第二种方式进行介绍。
ThreadPoolExecutor的构造函数及参数说明如下
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
|
corePoolSize:线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;如果当前线程数为corePoolSize,继续提交的任务会被保存到阻塞队列中,等待被执行;如果执行了线程池的prestartALLCoreThreads()方法,线程池会提前创建并启动所有核心线程。
maximumPoolSize:线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize;
keepAliveTime:线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间;默认情况下,该参数只在线程数大于corePoolSize时才有用;
unit:keepAliveTime的单位
workQueue:用来保存等待被执行的任务的阻塞队列,且任务必须实现Runable接口,在JDK中提供了如下队列模式;
1、ArrayBlockingQueue 一个基于数组结构的阻塞队列,此队列按FIFO原则对元素进行排序。
2、LinkedBlockingQueue 一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue。今天工厂方法Executors.newFixedThreadPool()使用了这个队列。
3、SynchronousQueue一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法
Executors.newCachedThreadPool使用了这个队列。
4、PriorityBlockingQueue一个具有优先级的无限阻塞队列。
threadFactory
:线程工厂,用于创建线程,一般用默认的即可。也可以通过线程工厂给每个创建出来的线程设置更有意义的名字。
handler
:拒绝策略,当队列和线程池都满了,说明线程池出于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。策略模式有其它如下:
- AbortPolicy:直接抛出异常,阻止系统正常工作。
- CallerRunsPolicy:只要线程未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。
- DiscardOldestPolicy:该策略将丢弃最老的一个请求,也就是即将被执行的一个人任务,并尝试再次提交当前任务。
- DiscardPolicy:该策略默默的丢弃无法处理对
- 当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。
2.2 第二种方式的使用
下面的代码摘取自OKHttp库中的代码,通过对
ThreadPoolExecutor设置
参数构造一个线程池。
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
|
2.3 第一种方式
第一种方式是通过工厂模式将线程池的创建从用户的new操作中解耦了,第一种方式创建的线程池的类型有如下:
1.初始化一个指定线程数的线程池,使用LinkedBlockingQueue作为阻塞队列,不过当线程池没有可执行任务时,也不会释放线程。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
|
同上初始一个指定数量的线程池,但是会要求传入一个自定义的线程工厂。
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
|
2.初始化一个可以缓存线程的线程池,默认缓存60是,线程池的线程数可到达interge.MAX_VALUE,即2147483647,内部使用SynchronousQueue作为阻塞队列。和newFixedThreadPool创建的线程池不同,newCachedThreadPool在没有任务执行时,当线程空闲时间超过KeepAliveTime,会自动释放线程资源,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销。
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
|
所以,在使用该线程池时,一定要注意控制并发的任务数,否则创建大量的线程可能导致严重的性能问题。
3.初始化的线程池中只有一个线程,如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行,内部使用LinkedBlockingQueue作为阻塞队列。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
|
4.初始化的线程池可以在指定的时间内周期性的执行所提交的任务,在实际的业务场景中可以使用该线程池定期的同步数据。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
|
2.4 第三种方式
第三种方式通过定义一个ExecutorService的实现,在该实现中完成对ExecutorService和Executor接口的实现。
1.关键接口函数execute的实现
public void execute(Runnable runnable) {
if (mIsShutdown) {
return;
}
mQueue.add(runnable);
synchronized (mRunningThreads) {
if (mRunningThreads.size() < mMaxThreadNum) {
RecyclableThread thread;
if (mThreadPoolName != null) {
thread = new WorkerableThread(mThreadPoolName
+ "-" + mThreadNum.getAndIncrement());
} else {
thread = new WorkerableThread();
}
mRunningThreads.add(thread);
thread.start();
}
}
}
|
2.
WorkerableThread
类的定义
private final class WorkerableThread extends Thread {
private Runnable mRunnable;
WorkerableThread() {
super();
}
WorkerableThread(String name) {
super(name);
}
@Override
public void run() {
Runnable r = null;
while (isRunning) {// 注意,若线程无效则自然结束run方法,该线程就没用了
synchronized (taskQueue) {
while (isRunning && taskQueue.isEmpty()) {// 队列为空
try {
taskQueue.wait(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (!taskQueue.isEmpty())
r = taskQueue.remove(0);// 取出任务
}
if (r != null) {
r.run();// 执行任务
}
finished_task++;
r = null;
}
}
// 停止工作,让该线程自然执行完run方法,自然结束
public void stopWorker() {
isRunning = false;
}
private Runnable getRunnable() {
return mRunnable;
}
}
|
附上一份自定义线程池的实现连接参考:https://www.cnblogs.com/kinghitomi/archive/2012/01/19/2327418.html