线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。
线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。
本文学习记录的线程池是JDK中提供的ThreadPoolExecutor类。
实际开发中项目中,禁止自己new线程。必须使用线程池来维护和创建线程。
核心:复用机制。提前创建好固定的线程一直在运行状态,实现复用,限制线程创建数量。
使用线程池可以带来一系列好处:
例子:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Threadpool {public static void main(String[] args) {ExecutorService executorService = Executors.newCachedThreadPool();for (int i = 0; i < 10; i++) {final int finalI = i;executorService.execute((new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+","+finalI);}}));}}
}//输出:
pool-1-thread-2,1
pool-1-thread-6,5
pool-1-thread-5,4
pool-1-thread-4,3
pool-1-thread-3,2
pool-1-thread-8,7
pool-1-thread-1,0
pool-1-thread-7,6
pool-1-thread-9,8
pool-1-thread-10,9
//上面程序发现没有复用,查看源码发现可以创建MAX_VALUE的线程,不能体现特点。
//改用Executors.newFixedThreadPool(),可以解决
corePoolSize:核心线程数量 一直保持运行的线程
线程池中的核心线程数,默认情况下核心线程一直存活在线程池中,如果将ThreadPoolExecutor 的allowCoreThreadTimeOut 属性设为true,如果线程池一直闲置并超过了keepAliveTime 所指定的时间,核心线程就会被终止。
maximumPoolSize:最大线程数,线程池允许创建的最大线程数
keepAliveTime:超出corePoolSize后创建的线程的存活时间。
unit:keepAliveTime时间单位
workQueue:任务队列,用于保存待执行的任务。
threadFactory:线程池内部创建线程所用的工厂。
handler:任务无法执行时的处理器。
所有任务的调度都是由execute方法完成的,这部分完成的工作是:检查现在线程池的运行状态、运行线程数、运行策略,决定接下来执行的流程,是直接申请线程执行,或是缓冲到队列中执行,亦或是直接拒绝该任务。其执行过程如下:
不会。例如:配置核心线程数corePoolSize为2、最大线程数maximumPoolSize为5,我们可以通过配置超出corePoolSize核心线程数后创建的线程存活时间例如60s。在60s内没有核心线程一直没有任务执行,则会停止该线程。
因为默认的Executors线程池底层是基于ThreadPoolExecutor构造函数封装的,采用无界队列存放缓存任务,会一直缓存任务容易发生内存溢出,会导致我们最大线程数失效。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;public class MyExecutors {private List workThreads;private BlockingDeque runnableDeque;private boolean isRun = true;/*最大线程数@param maxThreadCount*/public MyExecutors(int maxThreadCount, int dequeSize){//1.限制队列容量缓存runnableDeque = new LinkedBlockingDeque(dequeSize);//2.提前创建好固定的的线程一直在运行状态---死循环实现new ArrayList(maxThreadCount);for (int i = 0; i < maxThreadCount; i++) {new WorkThread().start();}}class WorkThread extends Thread{public void run(){while (isRun || runnableDeque.size()>0){Runnable runnable = runnableDeque.poll();if (runnable != null){runnable.run();}}}}public boolean execute(Runnable command){return runnableDeque.offer(command);}public static void main(String[] args) {MyExecutors myExecutors = new MyExecutors(2,2);for (int i = 0; i < 10; i++) {final int finalI = i;myExecutors.execute(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"," + finalI);}});}myExecutors.isRun = false;}
}
实际最多执行多少个任务 核心线程数+缓存队列的容量+最大线程数-核心线程数
1.AbortPolicy 丢弃任务,抛运行时异常
2.CallerRunsPolicy 执行任务
3.DiscardPolicy 忽视,什么都不会发生
4.DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
5.实现 RejectedExecutionHandler 接口,可自定义处理器
全面学习可参考:
Java线程池实现原理及其在美团业务中的实践