转载:线程池详解(通俗易懂超级好)_拉格朗日(Lagrange)的博客-CSDN博客_线程池
目录
线程池也是一种多线程的方式,处理过程就是将任务放到队列里面,通过线程池中的线程执行这些任务
(1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
(2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
(3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
线程池的真正实现类是 ThreadPoolExecutor,其构造方法有如下4种:
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue
workQueue) { - this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
- Executors.defaultThreadFactory(), defaultHandler);
- }
-
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue
workQueue, - ThreadFactory threadFactory) {
- this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
- threadFactory, defaultHandler);
- }
-
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue
workQueue, - RejectedExecutionHandler handler) {
- this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
- Executors.defaultThreadFactory(), handler);
- }
-
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue
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;
- }
- // 创建线程池
- ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,
- MAXIMUM_POOL_SIZE,
- KEEP_ALIVE,
- TimeUnit.SECONDS,
- sPoolWorkQueue,
- sThreadFactory);
- // 向线程池提交任务
- threadPool.execute(new Runnable() {
- @Override
- public void run() {
- ... // 线程执行的任务
- }
- });
- // 关闭线程池
- threadPool.shutdown(); // 设置线程池的状态为SHUTDOWN,然后中断所有没有正在执行任务的线程
- threadPool.shutdownNow(); // 设置线程池的状态为 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表


任务队列是基于阻塞队列实现的,即采用生产者消费者模式,在 Java 中需要实现 BlockingQueue 接口。但 Java 已经为我们提供了 7 种阻塞队列的实现:
注意有界队列和无界队列的区别:如果使用有界队列,当队列饱和时并超过最大线程数时就会执行拒绝策略;而如果使用无界队列,因为任务队列永远都可以添加任务,所以设置 maximumPoolSize 没有任何意义。
线程工厂指定创建线程的方式,需要实现 ThreadFactory 接口,并实现 newThread(Runnable r) 方法。该参数可以不用指定,Executors 框架已经为我们实现了一个默认的线程工厂:
- /**
- * The default thread factory.
- */
- private static class DefaultThreadFactory implements ThreadFactory {
- private static final AtomicInteger poolNumber = new AtomicInteger(1);
- private final ThreadGroup group;
- private final AtomicInteger threadNumber = new AtomicInteger(1);
- private final String namePrefix;
-
- DefaultThreadFactory() {
- SecurityManager s = System.getSecurityManager();
- group = (s != null) ? s.getThreadGroup() :
- Thread.currentThread().getThreadGroup();
- namePrefix = "pool-" +
- poolNumber.getAndIncrement() +
- "-thread-";
- }
-
- public Thread newThread(Runnable r) {
- Thread t = new Thread(group, r,
- namePrefix + threadNumber.getAndIncrement(),
- 0);
- if (t.isDaemon())
- t.setDaemon(false);
- if (t.getPriority() != Thread.NORM_PRIORITY)
- t.setPriority(Thread.NORM_PRIORITY);
- return t;
- }
- }
当线程池的线程数达到最大线程数时,需要执行拒绝策略。拒绝策略需要实现 RejectedExecutionHandler 接口,并实现 rejectedExecution(Runnable r, ThreadPoolExecutor executor) 方法。不过 Executors 框架已经为我们实现了 4 种拒绝策略:
嫌上面使用线程池的方法太麻烦?其实Executors已经为我们封装好了 4 种常见的功能线程池,如下:
创建方法的源码:
- public static ExecutorService newFixedThreadPool(int nThreads) {
- return new ThreadPoolExecutor(nThreads, nThreads,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue
()); - }
- public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
- return new ThreadPoolExecutor(nThreads, nThreads,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue
(), - threadFactory);
- }
使用示例:
- // 1. 创建定长线程池对象 & 设置线程池线程数量固定为3
- ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
- // 2. 创建好Runnable类线程对象 & 需执行的任务
- Runnable task =new Runnable(){
- public void run() {
- System.out.println("执行任务啦");
- }
- };
- // 3. 向线程池提交任务
- fixedThreadPool.execute(task);
创建方法的源码:
- private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
-
- public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
- return new ScheduledThreadPoolExecutor(corePoolSize);
- }
- public ScheduledThreadPoolExecutor(int corePoolSize) {
- super(corePoolSize, Integer.MAX_VALUE,
- DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
- new DelayedWorkQueue());
- }
-
- public static ScheduledExecutorService newScheduledThreadPool(
- int corePoolSize, ThreadFactory threadFactory) {
- return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
- }
- public ScheduledThreadPoolExecutor(int corePoolSize,
- ThreadFactory threadFactory) {
- super(corePoolSize, Integer.MAX_VALUE,
- DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
- new DelayedWorkQueue(), threadFactory);
- }
使用示例:
- // 1. 创建 定时线程池对象 & 设置线程池线程数量固定为5
- ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
- // 2. 创建好Runnable类线程对象 & 需执行的任务
- Runnable task =new Runnable(){
- public void run() {
- System.out.println("执行任务啦");
- }
- };
- // 3. 向线程池提交任务
- scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS); // 延迟1s后执行任务
- scheduledThreadPool.scheduleAtFixedRate(task,10,1000,TimeUnit.MILLISECONDS);// 延迟10ms后、每隔1000ms执行任务
创建方法的源码:
- public static ExecutorService newCachedThreadPool() {
- return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
- 60L, TimeUnit.SECONDS,
- new SynchronousQueue
()); - }
- public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
- return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
- 60L, TimeUnit.SECONDS,
- new SynchronousQueue
(), - threadFactory);
- }
使用示例:
- // 1. 创建可缓存线程池对象
- ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
- // 2. 创建好Runnable类线程对象 & 需执行的任务
- Runnable task =new Runnable(){
- public void run() {
- System.out.println("执行任务啦");
- }
- };
- // 3. 向线程池提交任务
- cachedThreadPool.execute(task);
创建方法的源码:
- public static ExecutorService newSingleThreadExecutor() {
- return new FinalizableDelegatedExecutorService
- (new ThreadPoolExecutor(1, 1,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue
())); - }
- public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
- return new FinalizableDelegatedExecutorService
- (new ThreadPoolExecutor(1, 1,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue
(), - threadFactory));
- }
使用示例:
- // 1. 创建单线程化线程池
- ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
- // 2. 创建好Runnable类线程对象 & 需执行的任务
- Runnable task =new Runnable(){
- public void run() {
- System.out.println("执行任务啦");
- }
- };
- // 3. 向线程池提交任务
- singleThreadExecutor.execute(task);

获取ExecutorService可以利用JDK中的Executors 类中的静态方法,常用获取方式如下:
static ExecutorService newCachedThreadPool() 创建一个默认的线程池对象,里面的线程可重用,且在第一次使用时才创建
static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
线程池中的所有线程都使用ThreadFactory来创建,这样的线程无需手动启动,自动执行;
static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池
static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
创建一个可重用固定线程数的线程池且线程池中的所有线程都使用ThreadFactory来创建。
static ExecutorService newSingleThreadExecutor()
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
创建一个使用单个 worker 线程的 Executor,且线程池中的所有线程都使用ThreadFactory来创建。
Executors 的 4 个功能线程池虽然方便,但现在已经不建议使用了,而是建议直接通过使用 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
- 1:编写任务类(MyTask),实现Runnable接口;
- 2:编写线程类(MyWorker),用于执行任务,需要持有所有任务;
- 3:编写线程池类(MyThreadPool),包含提交任务,执行任务的能力;
- 4:编写测试类(MyTest),创建线程池对象,提交多个任务测试;
- package com.itheima.demo01;
- /*
- 需求:
- 自定义线程池练习,这是任务类,需要实现Runnable;
- 包含任务编号,每一个任务执行时间设计为0.2秒
- */
- public class MyTask implements Runnable{
- private int id;
- //由于run方法是重写接口中的方法,因此id这个属性初始化可以利用构造方法完成
-
- public MyTask(int id) {
- this.id = id;
- }
-
- @Override
- public void run() {
- String name = Thread.currentThread().getName();
- System.out.println("线程:"+name+" 即将执行任务:"+id);
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("线程:"+name+" 完成了任务:"+id);
- }
-
- @Override
- public String toString() {
- return "MyTask{" +
- "id=" + id +
- '}';
- }
- }
-
- package com.itheima.demo01;
-
- import java.util.List;
-
- /*
- 需求:
- 编写一个线程类,需要继承Thread类,设计一个属性,用于保存线程的名字;
- 设计一个集合,用于保存所有的任务;
- */
- public class MyWorker extends Thread{
- private String name;//保存线程的名字
- private List
tasks; - //利用构造方法,给成员变量赋值
-
- public MyWorker(String name, List
tasks) { - super(name);
- this.tasks = tasks;
- }
-
- @Override
- public void run() {
- //判断集合中是否有任务,只要有,就一直执行任务
- while (tasks.size()>0){
- Runnable r = tasks.remove(0);
- r.run();
- }
- }
- }
-
- package com.itheima.demo01;
-
- import java.util.Collections;
- import java.util.LinkedList;
- import java.util.List;
-
- /*
- 这是自定义的线程池类;
- 成员变量:
- 1:任务队列 集合 需要控制线程安全问题
- 2:当前线程数量
- 3:核心线程数量
- 4:最大线程数量
- 5:任务队列的长度
- 成员方法
- 1:提交任务;
- 将任务添加到集合中,需要判断是否超出了任务总长度
- 2:执行任务;
- 判断当前线程的数量,决定创建核心线程还是非核心线程
- */
- public class MyThreadPool {
- // 1:任务队列 集合 需要控制线程安全问题
- private List
tasks = Collections.synchronizedList(new LinkedList<>()); - //2:当前线程数量
- private int num;
- //3:核心线程数量
- private int corePoolSize;
- //4:最大线程数量
- private int maxSize;
- //5:任务队列的长度
- private int workSize;
-
- public MyThreadPool(int corePoolSize, int maxSize, int workSize) {
- this.corePoolSize = corePoolSize;
- this.maxSize = maxSize;
- this.workSize = workSize;
- }
-
- //1:提交任务;
- public void submit(Runnable r){
- //判断当前集合中任务的数量,是否超出了最大任务数量
- if(tasks.size()>=workSize){
- System.out.println("任务:"+r+"被丢弃了...");
- }else {
- tasks.add(r);
- //执行任务
- execTask(r);
- }
- }
- //2:执行任务;
- private void execTask(Runnable r) {
- //判断当前线程池中的线程总数量,是否超出了核心数,
- if(num < corePoolSize){
- new MyWorker("核心线程:"+num,tasks).start();
- num++;
- }else if(num < maxSize){
- new MyWorker("非核心线程:"+num,tasks).start();
- num++;
- }else {
- System.out.println("任务:"+r+" 被缓存了...");
- }
- }
-
- }
-
- package com.itheima.demo01;
- /*
- 测试类:
- 1: 创建线程池类对象;
- 2: 提交多个任务
- */
- public class MyTest {
- public static void main(String[] args) {
- //1:创建线程池类对象;
- MyThreadPool pool = new MyThreadPool(2,4,20);
- //2: 提交多个任务
- for (int i = 0; i <30 ; i++) {
- //3:创建任务对象,并提交给线程池
- MyTask my = new MyTask(i);
- pool.submit(my);
- }
- }
- }
-
主要还是线程池中的逻辑,定义阻塞队列,每当有任务执行的时候,添加到队列中,如果当前线程数量小于核心线程数,就被核心线程执行,如果小于最大线程数,就被新创建的线程执行,否则的话缓存在队列里面,队列里面满了的话,就执行拒绝策略
1:利用Executors工厂类的静态方法,创建线程池对象;
2:编写Runnable或Callable实现类的实例对象;
3:利用ExecutorService的submit方法或ScheduledExecutorService的schedule方 法提交并执行线程任务
4:如果有执行结果,则处理异步执行结果(Future)
5:调用shutdown()方法,关闭线程池
LinkedBlockingQueue默认的最大任务数量是Integer.MAX_VALUE,非常大,可以理解为无限大吧;但是存在这种情况,当每个线程获取到一个任务后,执行时间比较长,导致workQueue里积压的任务越来越多,机器的内存使用不停的飙升,最后也会导致OOM