11_线程池

Posted root_zhb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了11_线程池相关的知识,希望对你有一定的参考价值。

1、线程池概述

由于线程的创建和回收这两个过程是非常消耗性能的,
利用池化思想,事先创建好一些线程放入线程池中,使用完之后线程池再回收线程,达到减小资源开销的目的。

主要特点:

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

2、架构

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

3、使用方式

3.1、一池N线程

• 线程池中的线程处于一定的量,可以很好的控制线程的并发量
• 线程可以重复被使用,在显示关闭之前,都将一直存在
• 超出一定量的线程被提交时候需在队列中等待

3.2、一池一线程(一个任务一个任务执行)

线程池中最多执行 1 个线程,之后提交的线程活动将会排在队列中以此
执行

3.3、可扩容线程池(线程池根据需求创建线程)

• 线程池中数量没有固定,可达到最大值(Interger. MAX_VALUE)
• 线程池中的线程可进行缓存重复利用和回收(回收默认时间为 1 分钟)
• 当线程池中,没有可用线程,会重新创建一个线程

public class ExecutorDemo 
    public static void main(String[] args) 
        //一池N线程
        ExecutorService threadPool1 = Executors.newFixedThreadPool(5);
        //一池一线程
        ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
        //一池可扩容线程
        ExecutorService threadPool3 = Executors.newCachedThreadPool();
        try
            for(int i=1;i<=20;i++)
                threadPool3.execute(()->
                    System.out.println(Thread.currentThread().getName()+"  办理业务");
                );
            
        catch (Exception e)
            e.printStackTrace();
        finally 
            threadPool3.shutdown();
        
    

4、底层原理、七个参数、拒绝策略

上述三种线程池内部均使用如下代码创建线程池

public ThreadPoolExecutor(int corePoolSize,       //核心线程数量
                          int maximumPoolSize,    //最大线程数量
                          long keepAliveTime,     //非核心线程空闲时的存活时间
                          TimeUnit unit,          //存活时间单位
                          BlockingQueue<Runnable> workQueue,   //存放提交但未执行任务的队列
                          ThreadFactory threadFactory,         //线程工厂
                          RejectedExecutionHandler handler)    // 等待队列满后的拒绝策略

JDK内置的拒绝策略:

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

6、工作流程

  1. 在创建了线程池后,线程池中的线程数为零
  2. 当调用 execute() 方法添加一个请求任务时,线程池做出如下判断:
    • 如果正在运行的线程数量小于 corePoolSize ,那么马上创建线程运行这个任务;
    • 如果正在运行的线程数量大于或等于 corePoolSize ,那么将这个任务放入队列;
    • 如果队列满了且正在运行的线程数量还小于 maximumPoolSize ,那么还是要创建非核心线程立即运行这个任务;
    • 如果队列满了且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。
  3. 当一个线程完成任务时,它会从队列中取下一个任务来执行
  4. 当一个线程无事可做超过一定时间(keepAliveTime)时,线程会判断:
    • 如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。
    • 所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

7、自定义线程池

为什么不允许适用不允许 Executors.的方式创建线程池,如下图

如下代码:线程池核心线程数量为2,最大为5,阻塞队列为3.
多运行几次,可以看到拒绝策略AbortPolicy的效果。

public class ThreadPoolDemo1 
    public static void main(String[] args) 
        ExecutorService threadService = new ThreadPoolExecutor(2,
                5,
                2L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        try
            for(int i=1;i<=10;i++)
                threadService.execute(()->
                    System.out.println(Thread.currentThread().getName()+"  办理业务");
                );
            
        catch (Exception e)
            e.printStackTrace();
        finally 
            threadService.shutdown();
        
    

以上是关于11_线程池的主要内容,如果未能解决你的问题,请参考以下文章

Windows核心编程:第11章 Windows线程池

C++11实现一个简单的线程池

线程池

线程池

Python3 从零单排28_线程队列&进程池&线程池

python第十一天-----补:线程池