线程池ThreadPool,线程池底层ThreadPoolExecutor方法七大参数,拒绝策略,以及实际开发中高并发下用到哪个线程池?

为什么要用线程池

在这里插入图片描述

基本的三个线程池的底层就是ThreadPoolExecutor类

ExecutorService threadPool = Executors.newFixedThreadPool(int);//固定线程数的线程池,,执行长期的任务,性能好
ExecutorService threadPool = Executors.newSingleThreadExecutor(); //单线程的线程池,,一个任务一个任务执行
ExecutorService threadPool = Executors.newCachedThreadPool(); //一池N线程,,,执行很多短期异步的任务

Executors是工具类,类似Arrays、Collections
在这里插入图片描述

底层ThreadPoolExecutor的重要参数:7大参数(面试一般问前5个)

在这里插入图片描述

在这里插入图片描述
int corePoolSize, 线程池中的常驻核心线程数
int maximumPoolSize, 线程池能够容纳同时执行的最大线程数,此值必须大于等于1(当核心线程数满了并且阻塞队列的线程数也满了,那么就会进行扩容,最大值是maximumPoolSize)
long keepAliveTime, 多余的空闲线程的存活时间,当前线程池数量超过corePoolSize时,
当空闲时间达到keepAliveTime值时,多余空闲线程会被销毁直到只剩corePoolSize个线程为止。
TimeUnit unit, keepAliveTime的单位
BlockingQueue workQueue, 任务队列,被提交但尚未被执行的任务。(候客区)
ThreadFactory threadFactory, 表示生成线程池中的工作线程的线程工厂,用于创建线程一般用默认的即可
RejectedExecutionHandler handler 拒绝策略,表示当前队列满了并且工作线程大于等于线程池的最大线程数(maximumPoolSize)时如何来拒绝请求执行的runnable的策略
在这里插入图片描述
当空闲线程空闲的时间超过keepAliveTime,就会被销毁,但是最少会存在corePoolsize数个线程,如下
在这里插入图片描述

线程池的底层工作原理(七大参数如何互动?)

在这里插入图片描述

在这里插入图片描述

文字讲解

在创建线程池后,等待提交过来的任务请求。
当调用execute()方法添加一个请求任务时,线程池会做如下判断:
2.2 如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务。
2.2 如果正在运行的线程数量大于corePoolSize,那么将这个任务加入阻塞队列
2.3 如果这时候阻塞队列满了且正在运行的线程数量小于maximumPoolSize,那么还是要创建非核心线程立刻执行这个任务。
2.4 如果阻塞队列满了且正在运行的线程数大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略(RejectedExecutionHandler)来执行。
当一个线程完成任务时,他会从阻塞队列中取出下一个任务执行。
当一个线程无事可做超过keepAliveTime时,线程池会判断:
当运行的线程数大于corePoolSize,那么这个线程就被叫停。
所以线程池的所有任务完成后它的线程数最终会收缩到corePoolSize的数量。

package JUC;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 线程池
 */
public class MyThreadPoolDemo {
    public static void main(String []args){
        //System.out.println(Runtime.getRuntime().availableProcessors());//查看cup核数

        //线程池的底层就是ThreadPoolExecutor类
        //Executors是工具类
        //newScheduledThreadPool带时间的
        //java获得线程的方法有4种,继承Thread类、实现Runable(没有返回值,不会抛异常,重新run()方法)、实现Callable(有返回值,会抛异常,重新call()方法)、线程池(ThreadPoolExecutor)

        //池化不用new
        ExecutorService threadPool = Executors.newFixedThreadPool(5);//固定线程数的线程池,,执行长期的任务,性能好
        //ExecutorService threadPool = Executors.newSingleThreadExecutor(); //单线程的线程池,,一个任务一个任务执行
        //ExecutorService threadPool = Executors.newCachedThreadPool(); //一池N线程,,,执行很多短期异步的任务
        //模拟10用户来办理业务,每个用户就是一个外部的请求线程,线程池里的线程是工作人员
        try{
            for (int i = 0; i < 10 ; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"\t办理业务");
                });
                //TimeUnit.MILLISECONDS.sleep(200);//在Cache线程池里,如果每次暂停0.2秒那么一个线程就可以处理过了,所以每次都是同一个线程,去掉延时就不是一个线程了
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }
}


线程池的拒绝策略RejectedExecutionHandler(四种)

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

那么实际的高并发下使用哪个线程池?(面试题)

在这里插入图片描述
以上三种高并发下都不会使用:(阿里java开发手册)
在这里插入图片描述
1)第一个图片可以看到newFixedThreadPool和SingleThreadPool的阻塞队列用的是LinkedBlockingQueue,Linked底层是链表,Integer.MAX_VALUE接近21亿,请求可以一直加到阻塞队列里,那么太多就会OOM。
2)CachedThreadPool和ScheduledThreadPool的第二个参数也就是最大线程数是Integer.MAX_VALUE,可能创建大量线程,导致OOM

接下来继续测试下每个拒绝策略:(自定义线程池后)

这是自定义的线程池:可以看到核心线程数为2,最大线程数为5,阻塞队列为3,那么允许请求的最大线程数为5+3=8个

// 自定义线程池
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                1L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

1.AbortPolicy():直接抛出RejectedExecutionException异常阻止系统正常运行

public class MyThreadPoolDemo {
    public static void main(String []args){

// 自定义线程池
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                1L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        try{
            for (int i = 1; i <= 10 ; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"\t办理业务");
                });
                //TimeUnit.MILLISECONDS.sleep(200);//在Cache线程池里,如果每次暂停0.2秒那么一个线程就可以处理过了,所以每次都是同一个线程,去掉延时就不是一个线程了
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }


结果:可以看到会抛异常。
在这里插入图片描述
2.CallerRunsPolicy():“调用者运行”一种调节机制,该策略既不会抛出任务,也不会抛出异常,而是将某些任务回退到调用调用者,从而降低新的任务流量。()

public class MyThreadPoolDemo {
    public static void main(String []args){

// 自定义线程池
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                1L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );

        try{
            for (int i = 1; i <= 9 ; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"\t办理业务");
                });
                //TimeUnit.MILLISECONDS.sleep(200);//在Cache线程池里,如果每次暂停0.2秒那么一个线程就可以处理过了,所以每次都是同一个线程,去掉延时就不是一个线程了
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }

结果:可以看到多出来的会回退给main线程,(这里可能出现1个main或者没有main,因为线程执行的太快,阻塞队列的已经被执行到了,所以可能有偏差)
在这里插入图片描述
3.DiscardOldestPolicy() :抛出队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务

public static void main(String []args){

// 自定义线程池
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                1L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()
        );

        try{
            for (int i = 1; i <= 10 ; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"\t办理业务");
                });
                //TimeUnit.MILLISECONDS.sleep(200);//在Cache线程池里,如果每次暂停0.2秒那么一个线程就可以处理过了,所以每次都是同一个线程,去掉延时就不是一个线程了
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }

在这里插入图片描述
4.DiscardPolicy() :直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种方案。

    public static void main(String []args){

// 自定义线程池
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                1L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardPolicy()
        );

        try{
            for (int i = 1; i <= 10 ; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"\t办理业务");
                });
                //TimeUnit.MILLISECONDS.sleep(200);//在Cache线程池里,如果每次暂停0.2秒那么一个线程就可以处理过了,所以每次都是同一个线程,去掉延时就不是一个线程了
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }

在这里插入图片描述

那么实际生产中,如何配置自定义线程池的参数呢?

在这里插入图片描述

1.CUP密集型:

在这里插入图片描述

2.IO密集型

2.1.
在这里插入图片描述

2.2
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值