当系统系统规模较小,我们可以不使用线程池。但是当系统到达一定规模,频繁的创建和销毁线程池会消耗很多资源。
合理利用线程池能够带来三个好处。
1降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
2提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
3提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
下面演示下线程池的基本的使用
public class ThreadPoolExecutorTest { public static void main(String[] args) { ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); Runnable runnable = null; for (int i = 0; i < 20; i++) { runnable = new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " run"); } }; executor.execute(runnable); } executor.shutdown(); } }
Jdk默认了实现了4种线程池。
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,默认是60秒,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
查看源码 ,都是调用
其本质是通过不同的参数初始化一个ThreadPoolExecutor对象,只是传递的参数不同罢了
具体参数解释如下:
corePoolSize 线程池中的核心线程数,
maximumPoolSize 线程池中允许的最大线程数
keepAliveTime 线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间
keepAliveTime的 单位 unit
workQueue
用来保存等待被执行的任务的阻塞队列,且任务必须实现Runable接口,在JDK中提供了如下阻塞队列:
ArrayBlockingQueue:基于数组的有界阻塞队列,按FIFO排序任务;
LinkedBlockingQuene:基于链表的无界阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
priorityBlockingQuene:具有优先级的无界阻塞队列;
handler
线程池的拒绝策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池内置了4种策略:
1、AbortPolicy:直接抛出异常,默认策略;
2、CallerRunsPolicy:用调用者所在的线程来执行任务;
3、DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
4、DiscardPolicy:直接丢弃任务;
当然我们也可以根据应用场景实现RejectedExecutionHandler接口,自定义拒绝策略,
实际开发中,一般先记录日志 再开一个定时去处理
执行流程图如下
======================================================================
原理解析
找到线程池核心的execute()方法 源代码如下
if (command == null) throw new NullPointerException(); //ctl是一个包装变量 包含了线程池的状态以及线程池中线程数 int c = ctl.get(); //workerCountOf 获取线程池的当前线程数 if (workerCountOf(c) < corePoolSize) { // 【步骤1】 if (addWorker(command, true)) return; c = ctl.get(); } if (isRunning(c) && workQueue.offer(command)) { // 【步骤2】 int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) //【步骤4】 reject(command); else if (workerCountOf(recheck) == 0) addWorker(null, false); } else if (!addWorker(command, false)) // 【步骤3】 reject(command); }
具体的执行流程如下:
--> 如果线程数小于corePoolSize,则执行addWorker方法创建新的线程执行任务 并返回 ;否则执行步骤2;
--> 如果线程池处于RUNNING状态,并且任务成功放入阻塞队列中,则执行步骤4,否则执行 步骤3
--> 再次检查线程池的状态,如果线程池没有RUNNING,并且从阻塞队列中删除任务,则执行reject方法处理任务;
--> 执行addWorker方法创建新的线程执行任务,如果addWoker执行失败,则执行reject方法处理任务;
接下来我们看看addWorker方法
addWorker分为2部分
retry: for (;;) { int c = ctl.get(); int rs = runStateOf(c); if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false; for (;;) { int wc = workerCountOf(c); //通过自旋的方式,判断要添加的Worker是否是true,如果是的话,那么 //则判断当前的workerCount是否大于corePoolsize,否则则判断是否大于//maximumPoolSize,如果满足的话,说明workerCount超出了线程池大小,直//接返回false 如果没有超出就cas让workerCount+1 如果成功就跳出循环 失//败就继续进行状态的判断 if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; if (compareAndIncrementWorkerCount(c)) break retry; c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) continue retry; // else CAS failed due to workerCount change; retry inner loop } } --------第一部分完 /*如果满足了的话,那么则创建一个新的Worker对象 满足状态就添加到works中 然后启动Worker中的线程开始执行任务*/ boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { w = new Worker(firstTask); final Thread t = w.thread; if (t != null) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { int rs = runStateOf(ctl.get()); if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); workers.add(w); int s = workers.size(); if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; } } finally { mainLock.unlock(); } if (workerAdded) { t.start(); workerStarted = true; } } } finally { if (! workerStarted) addWorkerFailed(w); } return workerStarted; }