Java线程池详解_JDK1.8.0_191

Java线程池


线程池的作用

1.重用存在的线程,减少处理多请求时线程创建、销毁产生的开销。

2.请求达到时工作线程通常已经存在,请求无需等待,提高系统的响应性。


Executors中线程池的静态工厂方法

1.newFixedThreadPool

创建一个定长的线程池,每当提交一个任务就创建一个线程,直到达到池的最大长度,这时线程池会保持长度不再变化。

2.newCachedThreadPool

创建一个可缓存的线程池,如果当前线程池的长度超过了处理的需要时,它可以灵活地回收空闲的线程,当需求增加时,它可以灵活添加新的线程。

3.newSingleThreadExecutor

创建一个单线程化的executor,它只创建唯一的工作者线程来执行任务,executor会保证任务依照任务队列所规定的顺序(FIFO、LIFO、优先级)执行。

4.newScheduledThreadPool

创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。


ThreadPoolExecutor类中线程池的构造函数

/**
 * 通过给定的初始化参数创建一个线程池(ThreadPoolExecutor)
 *
 * @param corePoolSize 线程池的核心线程数,核心线程即使空闲也不会被销毁,
* 除非allowCoreThreadTimeOut被设置
* @param maximumPoolSize 线程池中允许的最大线程数 * @param keepAliveTime 表示当前线程数大于核心线程数时,空闲线程被销毁前
* 等待新任务到达的最大时间 *
@param unit keepAliveTime参数的时间单位 * @param workQueue 在任务被执行前用来存放的队列,这个队列将仅仅存放execute方法提交的Runnable任务 * @param threadFactory executor创建新线程所用的工厂 * @param handler 由于线程数量和队列容量已满,执行被阻塞时的拒绝策略 * @throws IllegalArgumentException if one of the following holds:<br> * {@code corePoolSize < 0}<br> * {@code keepAliveTime < 0}<br> * {@code maximumPoolSize <= 0}<br> * {@code maximumPoolSize < corePoolSize} * @throws NullPointerException if {@code workQueue} * or {@code threadFactory} or {@code handler} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }

 线程池的参数

1.核心线程数:即使在空闲时也不会被销毁的线程数

2.最大线程数:线程池中允许的最大线程数

3.保持存活时间:空闲线程在被销毁之前等待新任务的时间

4.存活时间单位:等待时间的单位

5.工作队列:存放待执行任务的队列

6.线程工厂:新线程创建的工厂

7.拒绝策略:当线程数量和队列已满时采取的拒绝策略


工作队列

工作队列有多种,但都实现BlockQueue接口

队列类型 线程池类型 特点
LinkedBlockingQueue

FixedThreadPool

SingleThreadPool

使用的工作队列

基于链表的先进先出队列
SynchronousQueue CachedThreadPool使用的工作队列 不保存提交的任务,而是直接创建一个线程来执行新任务
DelayedWorkQueue ScheduledThreadPool使用的工作队列  

拒绝策略

1.AbortPolicy:直接丢弃任务并抛出RejectedExecutionException异常。

2.CallerRunsPolicy:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。

3.DiscardOldestPolicy:丢弃队列中最老的一个任务,也就是即将要执行的任务,并再次尝试提交当前任务。

4.DiscardPolicy:丢弃任务,不做任何处理。


 

 线程池任务处理策略

线程池的任务处理如下图所示

 

逻辑较为清晰,其实这是线程池execute()方法的逻辑,下面看一下源码:

//通过execute向线程池提交任务
public
void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get();
//如果当前线程数未达到核心线程数,则直接创建线程来执行新任务
if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); }
//否则将任务加入阻塞队列,这里进行双重检查,如果线程池已经关闭,则调用reject(),
//如果当前线程池线程数为0,则新建线程
if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) reject(command); else if (workerCountOf(recheck) == 0) addWorker(null, false); }
//如果加入阻塞队列失败,则尝试新建一个线程,如果失败了
//则说明线程池关闭了或者线程达到最大线程数,因此调用reject()
else if (!addWorker(command, false)) reject(command); }

线程池提供了两个方法,用来关闭线程池。

(1)shutdown():不会立即关闭线程池,但也不接受新的任务,等待队列中所有任务执行完毕后关闭。

(2)shutdownNow():立即终止线程池,并尝试打断正在执行的任务,清空工作队列,返回尚未执行的任务。


线程池的线程数应该如何设置

Nthreads = Ncpu * (1 + w/c)  w为阻塞时间,c为计算时间

IO密集型:w/c>1,因此线程数应该为cpu的数倍,但需要考虑线程所占内存,因此通常将线程数设置为cpu的2倍。

CPU密集型:w/c=0,因此线程数为CPU个数。通常将线程数设置为CPU+1,防止线程由于偶尔出现的原因而暂停。

 

 

 

 


 

posted @ 2019-07-23 18:57  、、、、、、、  阅读(1312)  评论(0编辑  收藏  举报