【JUC并发编程11】线程池

线程池

11.1 线程池概述

连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用

线程池(英语:thread pool)一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度

线程池的优势: 线程池做的工作只要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超过数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。

线程池的特点:

  1. 降低资源消耗: 通过重复利用已创建的线程降低线程创建和销毁造成的销耗。
  2. 提高响应速度: 当任务到达时,任务可以不需要等待线程创建就能立即执行。
  3. 提高线程的可管理性: 线程是稀缺资源,如果无限制的创建,不仅会销耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

11.2 线程池架构

Java 中的线程池是通过 Executor 框架实现的,该框架中用到了 Executor,Executors,ExecutorService,ThreadPoolExecutor 这几个类

在这里插入图片描述

说明:Executors为工具类,I为接口类,C为实现类

11.3 线程池使用方式

Executors.newFixedThreadPool(int):一池N线程

ExecutorService threadPool1 = Executors.newFixedThreadPool(5); //5个窗口

Executors.newSingleThreadExecutor():一池一线程

ExecutorService threadPool2 = Executors.newSingleThreadExecutor(); //一个窗口

Executors.newCachedThreadPool():一池可扩容根据需求创建线程

 ExecutorService threadPool3 = Executors.newCachedThreadPool();

执行线程execute()
关闭线程shutdown()

void execute(Runnable command);参数为Runnable接口类,可以通过设置lambda

具体案例代码案例

public class ThreadPoolTest {
    public static void main(String[] args) {
        ExecutorService threadPool1 = Executors.newFixedThreadPool(5);
        ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
        ExecutorService threadPool3 = Executors.newCachedThreadPool();

        // 是个顾客请求
        try{
            for (int i = 1; i <= 10; i++) {
                // 到此时执行execute()方法才创建线程
                threadPool2.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" 办理业务");
                });
            }
        }finally {
            // 关闭线程
            threadPool1.shutdown();
        }
    }
}

一池N线程输出结果为:

pool-1-thread-2 办理业务
pool-1-thread-1 办理业务
pool-1-thread-3 办理业务
pool-1-thread-5 办理业务
pool-1-thread-4 办理业务
pool-1-thread-2 办理业务
pool-1-thread-1 办理业务
pool-1-thread-4 办理业务
pool-1-thread-5 办理业务
pool-1-thread-3 办理业务

一池一线程输出结果为:

pool-2-thread-1 办理业务
pool-2-thread-1 办理业务
pool-2-thread-1 办理业务
pool-2-thread-1 办理业务
pool-2-thread-1 办理业务
pool-2-thread-1 办理业务
pool-2-thread-1 办理业务
pool-2-thread-1 办理业务
pool-2-thread-1 办理业务
pool-2-thread-1 办理业务

一池可扩容根据需求创建线程输出结果为:

pool-3-thread-1 办理业务
pool-3-thread-5 办理业务
pool-3-thread-4 办理业务
pool-3-thread-7 办理业务
pool-3-thread-2 办理业务
pool-3-thread-3 办理业务
pool-3-thread-8 办理业务
pool-3-thread-6 办理业务
pool-3-thread-9 办理业务
pool-3-thread-10 办理业务

11.4 线程池底层原则

通过查看上面三种方式创建对象的类源代码
都有 new 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.acc = System.getSecurityManager() == null ?
        null :
    AccessController.getContext();
    // 常驻线程数量(核心)
    this.corePoolSize = corePoolSize;
    // 最大线程数量
    this.maximumPoolSize = maximumPoolSize;
    // 阻塞队列(排队的线程放入)
    this.workQueue = workQueue;
    // 线程存活时间
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    // 线程工厂,用于创建线程 线程工厂,用于创建线程
    this.threadFactory = threadFactory;
    // 拒绝测试(线程满了
    this.handler = handler;
}

11.5 线程池的七个参数

具体代码中的七个参数讲解:

int corePoolSize,常驻线程数量(核心)

int maximumPoolSize,最大线程数量

long keepAliveTime,TimeUnit unit,线程存活时间

BlockingQueue workQueue,阻塞队列(排队的线程放入)

ThreadFactory threadFactory,线程工厂,用于创建线程

RejectedExecutionHandler handler,拒绝测试(线程满了)

具体工作流程是:

  1. 在执行创建对象的时候不会创建线程,创建线程的时候execute()才会创建
  2. 先到常驻线程,满了之后再到阻塞队列进行等待,阻塞队列满了之后,在往外扩容线程,扩容线程不能大于最大线程数。大于最大线程数和阻塞队列之和后,会执行拒绝策略。
  3. 阻塞队列为3,常驻线程数2,最大线程数5

11.6 线程池底层工作流程

在这里插入图片描述

对流程图的解释

现在假设来了9个线程,在执行execute()方法才创建线程。

第1-2个线程进入线程池创建

第3-5个线程进入阻塞队列

第6-8个线程会为他们创建新线程执行(直接运行线程6而非线程3)

第9个线程会被拒绝

总结来说:先到常驻线程,满了之后再到阻塞队列进行等待,阻塞队列满了之后,在往外扩容线程,扩容线程不能大于最大线程数。大于最大线程数和阻塞队列之和后,会执行拒绝策略。

具体的拒绝策略有:

  1. 抛异常-AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行
  2. 谁调用找谁-CallerRunsPolicy:“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量
  3. 抛弃最久执行当前-DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中,尝试再次提交当前任务
  4. 不理不问-Policydiscard:该策略默默地丢弃无法处理的任务,不予任何处理也不抱出异常。如果允许任务丢失,这是最好的一种策略

11.7 自定义线程池

实际在开发中不允许使用Executors创建,而是通过ThreadPoolExecutor的方式,规避资源耗尽风险

说明:Executors 返回的线程池对象的弊端如下:

1)FixedThreadPool 和 SingleThreadPool:

允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

2)CachedThreadPool 和 ScheduledThreadPool:

允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

ExecutorService threadPool = new ThreadPoolExecutor(
    // 常驻核心线程
        2,
    // 最大线程数量
        5,
    // 线程存活时间
        2L,
        TimeUnit.SECONDS,
    // 阻塞队列
        new ArrayBlockingQueue<>(3),
    // 线程工厂
        Executors.defaultThreadFactory(),
    // 拒绝策略
        new ThreadPoolExecutor.AbortPolicy()
);

其他都同理,只是调用ThreadPoolExecutor类,自定义参数

public class ThreadPoolTest {
    public static void main(String[] args) {
        // 组定义线程池
        ExecutorService threadPool = new ThreadPoolExecutor(
                // 常驻线程数量(核心)2个
                2,
                // 最大线程数量5个
                5,
                // 线程存活时间:2秒
                2L,
                TimeUnit.SECONDS,
                // 阻塞队列
                new ArrayBlockingQueue<>(3),
                // 默认线程工厂
                Executors.defaultThreadFactory(),
                // 拒绝策略。抛出异常
                new ThreadPoolExecutor.AbortPolicy()
        );

        try{
            for (int i = 1; i <= 8; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" 办理业务");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 关闭线程池
            threadPool.shutdown();
        }
    }
}

输出结果

pool-1-thread-1 办理业务
pool-1-thread-4 办理业务
pool-1-thread-3 办理业务
pool-1-thread-2 办理业务
pool-1-thread-3 办理业务
pool-1-thread-4 办理业务
pool-1-thread-1 办理业务
pool-1-thread-5 办理业务

如果线程数大于 最 大 线 程 数 量 + 阻 塞 队 列 容 量 最大线程数量+阻塞队列容量 线+ 则抛出异常

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_之桐_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值