Java线程池ThreadPoolExecutor初略探索
2019-11-06 17:39
1656 查看
在操作系统中,线程是一个非常重要的资源,频繁创建和销毁大量线程会大大降低系统性能。Java线程池原理类似于数据库连接池,目的就是帮助我们实现线程复用,减少频繁创建和销毁线程
ThreadPoolExecutor
ThreadPoolExecutor是线程池的核心类。首先看一下如何创建一个ThreadPoolExecutor。下面是ThreadPoolExecutor常用的一个构造方法:
构造方法
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; }
使用案例
/** * 阻塞的线程池 */ private ThreadPoolExecutor executor = new ThreadPoolExecutor( 0, // corePoolSize:线程池维护线程的最少数量 4, // maximumPoolSize:线程池维护线程的最大数量 10000, // keepAliveTime:线程池维护线程所允许的空闲时间 TimeUnit.MILLISECONDS, // unite:线程池维护线程所允许的空闲时间的单位 new LinkedBlockingQueue<>(200), // workQueue:线程池所使用的缓冲队列 new CallerBlockedPolicy() // handler:线程池对拒绝任务的处理策略,自定义拓展 );
构造方法参数说明
- corePoolSize:核心线程数量,当有新任务在
execute()
方法提交时,会执行以下判断: 如果运行的线程少于corePoolSize
,则创建新线程来处理任务,即使线程池中的其他线程是空闲的; - 如果线程池中的
1b4e
线程数量大于等于
corePoolSize
且小于maximumPoolSize
,则只有当workQueue
满时才创建新的线程去处理任务; - 如果设置的
corePoolSize
和maximumPoolSize
相同,则创建的线程池的大小是固定的,这时如果有新任务提交,若workQueue
未满,则将请求放入workQueue
中,等待有空闲的线程去从workQueue
中取任务并处理; - 如果运行的线程数量大于等于maximumPoolSize,这时如果workQueue已经满了,则通过handler所指定的策略来处理任务;
所以,任务提交时,判断的顺序为 corePoolSize –> workQueue –> maximumPoolSize。
corePoolSize的时候,把该任务封装成一个
Worker对象放入等待队列;
-
直接切换:这种方式常用的队列是
SynchronousQueue,但现在还没有研究过该队列,这里暂时还没法介绍;
LinkedBlockingQueue。如果使用这种方式,那么线程池中能够创建的最大线程数就是
corePoolSize,而
maximumPoolSize就不会起作用了(后面也会说到)。当线程池中所有的核心线程都是RUNNING状态时,这时一个新的任务提交就会放入等待队列中。
ArrayBlockingQueue。使用该方式可以将线程池的最大线程数量限制为
maximumPoolSize,这样能够降低资源的消耗,但同时这种方式也使得线程池对线程的调度变得更困难,因为线程池和队列的容量都是有限的值,所以要想使线程池处理任务的吞吐率达到一个相对合理的范围,又想使线程调度相对简单,并且还要尽可能的降低线程池对资源的消耗,就需要合理的设置这两个数量。 如果要想降低系统资源的消耗(包括CPU的使用率,操作系统资源的消耗,上下文环境切换的开销等), 可以设置较大的队列容量和较小的线程池容量, 但这样也会降低线程处理任务的吞吐量。
setMaximumPoolSize()方法来重新设定线程池的容量。
corePoolSize的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了
keepAliveTime;
Executors.defaultThreadFactory()来创建线程。使用默认的- - - - ThreadFactory来创建线程时,会使新创建的线程具有相同的NORM_PRIORITY优先级并且是非守护线程,同时也设置了线程的名称。
RejectedExecutionHandler类型的变量,表示线程池的饱和策略。如果阻塞队列满了并且没有空闲的线程,这时如果继续提交任务,就需要采取一种策略处理该任务。线程池提供了4种策略:
-
AbortPolicy:直接抛出异常,这是默认策略;
线程池的监控
通过线程池提供的参数进行监控。线程池里有一些属性在监控线程池的时候可以使用
- getTaskCount:线程池已经执行的和未执行的任务总数;
- getCompletedTaskCount:线程池已完成的任务数量,该值小于等于taskCount;
- getLargestPoolSize:线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过,也就是达到了 maximumPoolSize;
- getPoolSize:线程池当前的线程数量;
- getActiveCount:当前线程池中正在执行任务的线程数量。
总结一下线程池添加任务的整个流程:
- 线程池刚刚创建是,线程数量为0;
- 执行execute添加新的任务时会在线程池创建一个新的线程;
- 当线程数量达到corePoolSize时,再添加新任务则会将任务放到workQueue队列;
- 当队列已满放不下新的任务,再添加新任务则会继续创建新线程,但线程数量不超过maximumPoolSize;
- 当线程数量达到maximumPoolSize时,再添加新任务则会抛出异常。
相关文章推荐
- 探索JAVA并发 - 线程池详解
- Java线程池ThreadPoolExecutor深度探索及源码解析
- Java 线程池艺术探索
- Java 线程池艺术探索
- Java 线程池艺术探索
- JAVA 线程池探索之路
- Java线程池ThreadPoolExecutor深度探索及源码解析
- Java 线程池的原理与实现
- Java线程池
- Java(Android)线程池,介绍new Thread的弊端及Java四种线程池的使用
- 如何创建Java中的线程池
- 转Java线程池
- Java高并发编程:线程池
- JAVA线程池管理的实现
- 一个简单的JAVA线程池(只是用实验)
- Java线程池
- java自带线程池和队列详细讲解
- 有关Java中线程池的介绍
- Java(Android)线程池
- java线程池-2