Java线程池
来源:互联网 发布:linux 编译动态库 编辑:程序博客网 时间:2024/06/10 15:10
- 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.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler;}
构造方法参数:
- corePoolSize:核心池大小。默认情况下,创建一个线程池初始线程个数为0,当有任务的时候才会去创建线程,当创建的线程数达到corePoolSize后,就会把新到达的任务放到阻塞队列中。除了调了prestartAllCoreThreads()或者prestartCoreThread()方法,这两个方法会在初始化的时候就创建所有核心线程或1个核心线程。
- maximumPoolSize:最大线程数。表示线程池最多能创建的线程数量。
- keepAliveTime:线程空闲时间。默认情况下只有当线程数超过了corePoolSize这个参数才会起作用,一个线程的空闲时间如果超过了keepAliveTime,就会被销毁,直到线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean),keepAliveTime参数会一直生效,直到线程池数量为0.
- unit:keepAliveTime的时间单位。在TimeUnit类中有7种静态属性。
- workQueue:阻塞队列。用于存储等待执行的任务。一般有
ArrayBlockingQueue;LinkedBlockingQueue;SynchronousQueue;
* threadFactory:线程工厂。用于创建线程。
* handler:线程池拒绝处理任务时策略。一般有:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
线程池实现原理
线程池状态
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
这里有个很关键的字段ctl,因为这个变量控制了线程状态和线程数量,且具有原子性。很神奇,看看源码注释是怎么说的:
/*** The main pool control state, ctl, is an atomic integer packing* two conceptual fields* workerCount, indicating the effective number of threads* runState, indicating whether running, shutting down etc** In order to pack them into one int, we limit workerCount to* (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2* billion) otherwise representable. If this is ever an issue in* the future, the variable can be changed to be an AtomicLong,* and the shift/mask constants below adjusted. But until the need* arises, this code is a bit faster and simpler using an int.*/
由于一个int类型是由32位的二进制数构成的,这里很巧妙的利用高3位表示runState,用低29位表示workerCount。这段代码让我感觉这个类非常的精细。解释称2^29 - 1 大概5亿。如果之后不够的话可以将int类型改成long,但是现在使用int会比较简单有效。
以下将介绍几个ThreadPoolExecutor类中常见且相关的几个方法:
private static int ctlOf(int rs, int wc) { return rs | wc; }
其中rs就是高三位的runState,wc就是低29位的workerCount。
private static int runStateOf(int c) { return c & ~CAPACITY; }private static int workerCountOf(int c) { return c & CAPACITY; }
c就是前面ctl的值。这两个方法分别用来获取runState和workerCount。
其中CAPACITY的定义:
private static final int COUNT_BITS = Integer.SIZE - 3;private static final int CAPACITY = (1 << COUNT_BITS) - 1;
可以看出CAPACITY其实就是workerCount的最大值。
/*** The runState provides the main lifecyle control, taking on values:** RUNNING: Accept new tasks and process queued tasks* SHUTDOWN: Don't accept new tasks, but process queued tasks* STOP: Don't accept new tasks, don't process queued tasks,* and interrupt in-progress tasks* TIDYING: All tasks have terminated, workerCount is zero,* the thread transitioning to state TIDYING* will run the terminated() hook method* TERMINATED: terminated() has completed*/
- RUNNING:可以接收新任务和处理阻塞列表中的任务
- SHUTDOWN:不能接收新任务,但是会继续处理阻塞列表中的任务
- STOP:不能接收新任务且也不会再处理阻塞列表中的任务
- TIDYING:所有任务都结束了,workCount为0,并且会调用terminated()
- TERMINATED:terminated()调用结束
// runState is stored in the high-order bitsprivate static final int RUNNING = -1 << COUNT_BITS;private static final int SHUTDOWN = 0 << COUNT_BITS;private static final int STOP = 1 << COUNT_BITS;private static final int TIDYING = 2 << COUNT_BITS;private static final int TERMINATED = 3 << COUNT_BITS;
代码中会有rs < SHUTDOWN 的判断,其实就是指RUNNING
示例
/** * Created by cxx on 2017/6/16. */public class TreadPoolTest { public static void main(String[] args) { ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5),new ThreadPoolExecutor.DiscardPolicy()); System.out.println("启动后线程数量:"+pool.getPoolSize()); for(int i=0;i<20;i++){ for (;;){ if (pool.getQueue().size() <5 || pool.getPoolSize() < 10){ Task myTask = new Task(i); pool.execute(myTask); System.out.println("线程池数:"+pool.getPoolSize()+",队列数:"+ pool.getQueue().size()); break; } } } pool.shutdown(); }}class Task implements Runnable { private int taskNum; public Task(int num) { this.taskNum = num; } @Override public void run() { System.out.println("开始执行 "+taskNum); try { Thread.currentThread().sleep(3000); } catch (InterruptedException e) { System.out.println("被中断啦。。。。。。。。。。"); } System.out.println("task "+taskNum+"执行完毕"); }}
运行结果
启动后线程数量:0线程池数:1,队列数:0开始执行 0线程池数:2,队列数:0开始执行 1线程池数:3,队列数:0开始执行 2线程池数:4,队列数:0开始执行 3线程池数:5,队列数:0开始执行 4线程池数:5,队列数:1线程池数:5,队列数:2线程池数:5,队列数:3线程池数:5,队列数:4线程池数:5,队列数:5线程池数:6,队列数:5开始执行 10线程池数:7,队列数:5开始执行 11线程池数:8,队列数:5开始执行 12线程池数:9,队列数:5开始执行 13线程池数:10,队列数:5开始执行 14task 0执行完毕task 4执行完毕开始执行 5task 3执行完毕task 1执行完毕开始执行 8task 2执行完毕开始执行 9task 12执行完毕开始执行 7task 11执行完毕task 10执行完毕开始执行 6task 14执行完毕task 13执行完毕线程池数:10,队列数:1开始执行 15开始执行 16线程池数:10,队列数:1线程池数:10,队列数:1开始执行 17开始执行 18线程池数:10,队列数:1线程池数:10,队列数:1开始执行 19task 8执行完毕task 9执行完毕task 5执行完毕task 7执行完毕task 6执行完毕task 15执行完毕task 16执行完毕task 17执行完毕task 19执行完毕task 18执行完毕Process finished with exit code 0
看运行结果可以看到,当核心数达到corePoolSize,就会往workQueue添加,workQueue满了之后,就会创建新的线程直到线程数达到maximumPoolSize。
在嵌套循环中判断线程和队列是否饱和,如果饱和就等会在添加。(现在添加的话会被丢弃,因为策略配置的是丢弃并且不抛异常)
合理配置线程池大小
在javadoc中并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态类:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());}
固定线程数线程池,核心和最大线程数都一样。由于keepAliveTime为0,所以一旦线程启动后除非被shutdown,否则会一直等待新任务。队列是一个没有大小限制的队列,你唯一要担心的是内存够不够用。
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));}
相当于固定线程数线程池的线程数为1.但是我还注意到这里返回的是FinalizableDelegatedExecutorService类。好奇心让我点开了这个类:
static class FinalizableDelegatedExecutorService extends DelegatedExecutorService { FinalizableDelegatedExecutorService(ExecutorService executor) { super(executor); } protected void finalize() { super.shutdown(); }}
是一个Executors的内部类,构造函数调用父类的构造方法,覆盖了finalize方法,用于垃圾回收时触发shutdown。(这一点暂时不去深究,为什么要去这么做)。在点开父类:
static class DelegatedExecutorService extends AbstractExecutorService { private final ExecutorService e; DelegatedExecutorService(ExecutorService executor) { e = executor; } public void execute(Runnable command) { e.execute(command); } public void shutdown() { e.shutdown(); } public List<Runnable> shutdownNow() { return e.shutdownNow(); } public boolean isShutdown() { return e.isShutdown(); } public boolean isTerminated() { return e.isTerminated(); } public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return e.awaitTermination(timeout, unit); } public Future<?> submit(Runnable task) { return e.submit(task); } public <T> Future<T> submit(Callable<T> task) { return e.submit(task); } public <T> Future<T> submit(Runnable task, T result) { return e.submit(task, result); } public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { return e.invokeAll(tasks); } public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException { return e.invokeAll(tasks, timeout, unit); } public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException { return e.invokeAny(tasks); } public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return e.invokeAny(tasks, timeout, unit); }}
也是Executors的内部类,这里有个设计概念,代理。
把被代理对象ExecutorService作为私有成员变量,然后仅暴露想暴露的方法。(学习了,自己以后开发过程中也可以这么用)
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());}
按需自动扩容线程池。核心线程数为0,keepAliveTime为60s,队列为同步队列。适合执行时间短,任务量大的场景。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize);}/** * Creates a new {@code ScheduledThreadPoolExecutor} with the * given core pool size. * * @param corePoolSize the number of threads to keep in the pool, even * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @throws IllegalArgumentException if {@code corePoolSize < 0} */public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS, new DelayedWorkQueue());}
可以创建一个延迟或周期执行任务的线程池。(具体实现还没细究)
总结一下,几种线程池对于使用方面还是比较容易区分,但是具体该配置多少线程数,这个需要实践才能得出结果(基础环境和业务环境千变万化)。一般可以根据任务的类型类配置(网上的得来的经验,参考一下,使用的时候还是得多多调试。):
CPU密集型型的任务,就要压榨CPU,不要频繁切换,参考值:cpu核心数+1
IO密集型的人物,参考值:2*cpu核心数
最后再强调一下,不要偷懒,一定要经过调试再选择正如的线程数量。
- Java线程:线程池
- java--线程--线程池
- Java线程(六):线程池
- Java线程(五):线程池
- Java线程(五):线程池
- Java线程_07_线程池
- Java线程(五):线程池
- Java线程(六):线程池
- Java线程(五):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(五):线程池
- Java线程(六):线程池
- AsyncTask的使用及原理
- 数据库事务隔离级别,ACID,spring事务传播性
- date时间格式化 只想要年月日
- 跨浏览器的事件处理(套装)
- MySQL索引及查询优化总结
- Java线程池
- Java记事本
- Android Shape绘制实用圆圈,并动态改变圆点的颜色
- 黄色填充问题input
- 数据结构基础温故-1.线性表(上)
- 中国互联网乃至移动互联网发展最好的十个城市排名,我会这么排:北京,深圳,上海,杭州,广州,成都,武汉,南京,西安,厦门。
- centos7搭建gitlab(一)
- Spring Data MongoDB 六:进阶Aggregation操作(上)
- # 美团点评CodeM编程大赛-题三