为什么要使用线程池?
1、创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率。
例如:记创建线程消耗时间T1,执行任务消耗时间T2,销毁线程消耗时间T3
如果T1+T3>T2,那么是不是说开启一个线程来执行这个任务太不划算了!
正好,线程池缓存线程,可用已有的闲置线程来执行新任务,避免了T1+T3带来的系统开销。
2、线程并发数量过多,抢占系统资源从而导致阻塞。
我们知道线程能共享系统资源,如果同时执行的线程过多,就有可能导致系统资源不足而产生阻塞的情况。
运用线程池能有效的控制线程最大并发数,避免以上的问题。
3、对线程进行一些简单的管理,比如:延时执行、定时循环执行的策略等,运用线程池都能进行很好的实现。
所以,在《阿里巴巴java开发手册》中指出了线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程。
这样一方面是线程的创建更加规范,可以合理控制开辟线程的数量;另一方面线程的细节管理交给线程池处理,优化了资源的开销。
那我们就按照手册来吧:
如何创建一个线程池呢?
在 Java 中,新建一个线程池对象非常简单,Java 本身提供了工具类 java.util.concurrent.Executors。
可以使用如下代码创建一个固定数量线程的线程池:
ExecutorService service = Executors.newFixedThreadPool(10);
注意:以上代码用来测试还可以,实际使用中最好能够显示地指定相关参数。
我们可以看下其内部源码实现:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
在阿里巴巴代码规范中,建议我们自己指定线程池的相关参数,为的是让开发人员能够自行理解线程池创建中的每个参数。
根据实际情况,创建出合理的线程池。接下来,我们来剖析下 java.util.concurrent.ThreadPoolExecutor 的构造方法参数。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
构造函数的参数含义如下:
corePoolSize: 指定了线程池中的线程数量,它的数量决定了添加的任务是开辟新的线程去执行,还是放到workQueue任务队列中去;
maximumPoolSize: 指定了线程池中的最大线程数量,这个参数会根据你使用的workQueue任务队列的类型,决定线程池会开辟的最大线程数量;
keepAliveTime: 当线程池中空闲线程数量超过corePoolSize时,多余的线程会在多长时间内被销毁;
unit: keepAliveTime的单位;
workQueue: 任务队列,被添加到线程池中,但尚未被执行的任务;它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种;
threadFactory: 线程工厂,用于创建线程,一般用默认即可; // 后面默认的参数 defaultThreadFactory()
handler: 拒绝策略,当任务太多来不及处理时,如何拒绝任务; // 后面默认的参数 defaultHandler

浙公网安备 33010602011771号