对于线程池主要关心的内容有:线程池的大小控制、池中的队列控制、池饱和时的策略,下面我们从这三个角度来分析一下Executors生成的不同池。
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:JDK文档里定义为核心线程数,很耐人寻味的一个词,但是很重要,我们在下面的分析中经常会使用它,对于它的理解,我们最后在取总结;
第二个maximumPoolSize:线程池中可容纳的最大线程个数;
第三个keepAliveTime:当当前的线程数大于核心线程数时,
多余的空闲线程等待新任务的最长时间,如果没有新任务进入则多余的空闲线程将被销毁;
第四个unit:第三个参数的单位;
第五个workQueue:保持任务的队列(任务队列基本有三种形式:无限队列、有限队列和同步移交);
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
相对第一个构造函数多了一个handler,这个参数用来指定线程池饱和时的策略(饱和策略主要有:中止、遗弃、遗弃最旧的、调用者运行);还有两个构造函数这里就不再列出了,不同点就是可以指定threadFactory。
2、newCachedThreadPool:
有了上面的知识,我们来分析一下newCachedThreadPool构造的线程池,先看一下方法内容:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
这里指定了,核心线程数为0,线程池中最大的线程数为0x7fffffff,当前线程个数大于核心线程数时,多余线程的存活时间为60秒,任务队列指定的使用的是SynchronousQueue,饱和策略使用的是AbortPolicy(中止策略)
下面我们来看一下newCachedThreadPool创建的线程池:线程池大小、池中的队列、池饱和时的策略
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
if (runState == RUNNING && workQueue.offer(command)) {
if (runState != RUNNING || poolSize == 0)
ensureQueuedTaskHandled(command);
}
else if (!addIfUnderMaximumPoolSize(command))
reject(command); // is shutdown or saturated
}
}
上面的代码是线程池执行任务的核心代码,我们分析一下,
poolSize记录的是当前线程池中的线程个数,corePoolSize是我们指定的线程池的核心线程个数,在newCachedThreadPool创建的线程池中poolSize >= corePoolSize永远都会成立,因为corePoolSize被我们设置成0,当前线程池的大小永远是大于等于0的,接下来看一下内层代码,先判断一下当前线程池的状态是否在运行状态,然后调用workQueue.offer(command)方法,在newCachedThreadPool创建的线程池中workQueue是SynchronousQueue,因此调用的是SynchronousQueue的如下代码:
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
return transferer.transfer(e, true, 0) != null;
}
这里的transferer是TransferStack,由于我们timed和nanos(transfer方法的第二和第三个参数)设置为true和0,因此会永远执行transfer方法的如下代码:
if (timed && nanos <= 0) { // can't wait
if (h != null && h.isCancelled())
casHead(h, h.next); // pop cancelled node
else
return null;
}
而这里transferer.transfer(e, true, 0)方法的返回是null,因此workQueue.offer(command)方法永远是false。
这样机只会执行:
else if (!addIfUnderMaximumPoolSize(command))
reject(command);
这个分支,addIfUnderMaximumPoolSize方法如下:
private boolean addIfUnderMaximumPoolSize(Runnable firstTask) {
Thread t = null;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (poolSize < maximumPoolSize && runState == RUNNING)
t = addThread(firstTask);
} finally {
mainLock.unlock();
}
if (t == null)
return false;
t.start();
return true;
}
这里又会判断poolSize当前线程池中线程个数是否小于maximumPoolSize线程池中可容纳的最大个数,如果小于则创建一个线程来执行对应的任务返回true,如果不小于则会返回false,当返回false时,回去执行reject(command)方法,会根据中止的策略来处理新提交的任务。
这里要说明一下SynchronousQueue:
SynchronousQueue并不是一个队列,只是线程之间移交信息的机制,当我们把一个元素放入到SynchronousQueue中时必须有另一个线程正在等待接受移交的任务,如果没有这样的线程存在,只要当前池的大小还小于最大值,线程池就会创建一个新的线程来执行这个任务,因此使用SynchronousQueue作为线程池队列的前提是,要么池的大小是无限的或者可以接受被拒绝策略。
例子:
public class CachedThreadPoolTest {
/**
* @param args
*/
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
((ThreadPoolExecutor)service).setMaximumPoolSize(3);
((ThreadPoolExecutor)service).setCorePoolSize(2);
((ThreadPoolExecutor)service).setKeepAliveTime(20, TimeUnit.SECONDS);
service.submit(new SleepTask());
service.submit(new SleepTask());
service.submit(new SleepTask());
try{
service.submit(new SleepTask());--将被拒绝
}catch(RejectedExecutionException e){
System.out.println("Rejected1");
}
try{
service.submit(new SleepTask());--将被拒绝
}catch(RejectedExecutionException e){
System.out.println("Rejected2");
}
}
}
结果:
Rejected1
Rejected2
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
SleepTask代码
public class SleepTask implements Callable<String> {
@Override
public String call() throws Exception {
TimeUnit.SECONDS.sleep(10);
System.out.println(Thread.currentThread().getName());
return Thread.currentThread().getName();
}
}
分享到:
相关推荐
Java中Executors类中几种创建各类型线程池方法及简单实例
线程是系统中可执行调度的...线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,规避资源耗尽的风险。集合的详细描述,以及集合中的异同点,HashMap不同jdk版本区别,ConcurrentHashMap介绍。
而线程池不允许使用Executors去创建,而要通过ThreadPoolExecutor方式,这一方面是由于jdk中Executor框架虽然提供了如newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()等创建线程池的方法,...
Executors一共可以创建下面这四类线程池: 1.newFixedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。`` 2.newFixedThreadPool 创建一个定长线程池,...
Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 newFixedThreadPool 创建一个定长线程池...
【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors返回的线程池对象的弊端如下: 1) ...
Reference: 《创建Java线程池》[1],《Java线程:新特征-线程池》[2], 《Java线程池学习》[3],《线程池ThreadPoolExecutor使用简介》[4],《Java5中的线程池实例讲解》[5],《ThreadPoolExecutor使用和思考》[6] ...
ThreadPoolManager:Executors.newFixedThreadPool(num * 2);// 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程 threadPool: 线程池 创建线程池,销毁线程池,添加新任务
ExecutorService基于池化的线程来执行用户提交的任务,通常可以简单的通过Executors提供的工厂方法来创建ThreadPoolExecutor实例。 线程池解决的两个问题:1)线程池通过减少每次做任务的时候产生的性能消耗来...
线程池设计思路,java提供自带的线程池类ThreadPoolExecutor详解,Executors调用静态方法创建线程池
Java并发编程常见知识点源码集锦,涉及到对象锁,Executors多任务线程框架,线程池等示例,列出一些源码包中包括的内容: volatile关键字的非原子性、volatile关键字的使用、AtomicInteger原子性操作、线程安全小...
//创建线程池 //Runtime的availableProcessors()方法返回当前系统的CPU的数目 //系统的CPU越多,线程池中工作线程的数目也越多 executorService= Executors.newFixedThreadPool( Runtime.getRuntime()....
前言 这周我投递出了简历,岗位是java后端开发工程师。...Excutors创建线程池便捷方法如下: Executors.newFixedThreadPool(100);//创建固定大小的线程池 Executors.newSingleThreadExecutor();//创
最近阿里发布的 Java开发手册中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 其中ThreadFactoryBuilder...
2.1 创建线程池 2.2 提交任务 2.3 关闭线程池 线程池的分类 3.1 FixedThreadPool 3.2 CachedThreadPool 3.3 ScheduledThreadPool 3.4 SingleThreadExecutor 3.5 WorkStealingPool 线程池的参数配置 4.1 核心线程数 ...
创建工作线程通常很昂贵,线程池维护线程并允许您即时提交工作,而不必支付重新创建线程的费用。 使用节点的新worker_thread API,池中的线程可以相互传递消息,以及对共享内存进行读写。 用法 完整的API文档可以在...
JDK中提供了一个线程池工厂: Executors ,很多工厂方法, 可以创建多种线程池 1.单一线程池 ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor(); 该线程池只有一个线程, 若多个任务...
为什么阿里巴巴禁止使用Executors创建线程池?31 为什么阿里巴巴要求谨慎使用ArrayList中的subList方法?37 为什么阿里巴巴不建议在for循环中使用“+”进行字符串拼接?44 为什么阿里巴巴禁止在foreach循环里进行...
该项目是通过使用缓存线程池进行并行处理的示例。 假设我们将关键字列表传递给 Web 服务,该服务根据该关键字返回数据。 如果我们有几千个关键字,那么遍历它们将是一个昂贵且漫长的过程。 同时加载所有这些也不是一...
为什么阿里巴巴禁止使用 Executors 创建线程池? 31 为什么阿里巴巴要求谨慎使用 ArrayList 中的 subList 方法? 37 为什么阿里巴巴不建议在 for 循环中使用“+”进行字符串拼接? 44 为什么阿里巴巴禁止在 ...