美文网首页
6、线程池详解

6、线程池详解

作者: i小雨 | 来源:发表于2020-11-19 16:33 被阅读0次

线程池:3大方法、7大参数、4种拒绝策略

线程池的好处:

  • 降低资源的消耗
  • 提高响应速度
  • 方便统一管理
    线程复用、可以控制最大并发数、管理线程

线程池:3大方法

图片.png
  • Executors.newSingleThreadExecutor();//单个线程
  • Executors.newFixedThreadPool(5);//创建一个固定大小的线程池
  • Executors.newCachedThreadPool();//可伸缩的,遇强则强,遇弱则弱
public class TestPool1 {

    public static void main(String[] args) {
        //ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
        //ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建一个固定大小的线程池
        ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩的,遇强则强,遇弱则弱

        try {
            for (int i = 0; i < 10; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"->OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }

}

下面从7大参数种分析为什么不用Executors创建线程池

7大参数

源码分析:

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE, //约为21亿,容易造成OOM
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

本质:三大方法底层都是调用的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;
    }

可以发现ThreadPoolExecutor(...)有7个参数

  • int corePoolSize/ /核心线程池大小
  • int maximumPoolSize //最大线程池大小
  • long keepAliveTime //超时了没有人调用就会释放
  • TimeUnit unit //超时单位
  • BlockingQueue<Runnable> workQueue //阻塞队列
  • ThreadFactory threadFactory //线程工厂
  • RejectedExecutionHandler handler //拒绝策略


    图片.png

可以发现:通过Executors创建的newCachedThreadPool(...)的最大线程池数为Integer.MAX_VALUE, //约为21亿,容易造成OOM

用ThreadPoolExecutor创建线程池:(用的时默认的拒绝策略)

public static void test2(){
        //自定义线程池。工作中用ThreadPoolExecutor
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()   //默认的拒绝策略:如果阻塞队列满了,将不会处理新进来的线程,并报错。
        );

        try {
            //最大承载:maximumPoolSize+ Queue
            //当大于最大承载时会报错
            for (int i = 0; i < 8; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"->OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }

四种拒绝策略:

图片.png
  • AbortPolicy():
    默认的拒绝策略:如果阻塞队列满了,将不会处理新进来的线程,并报错。(代码如上)
  • CallerRunsPolicy() :
//用拒绝策略----CallerRunsPolicy()创建线程池
    public static void test3(){
        //自定义线程池。工作中用ThreadPoolExecutor
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy()  //哪来的去哪里
        );

        try {
            //最大承载:maximumPoolSize+ Queue
            for (int i = 0; i < 9; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"->OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
***********结果***********
pool-1-thread-1->OK
main->OK
pool-1-thread-1->OK
pool-1-thread-1->OK
pool-1-thread-1->OK
pool-1-thread-2->OK
pool-1-thread-3->OK
pool-1-thread-4->OK
pool-1-thread-5->OK

可以发现:第9个线程通过main线程执行的
只要线程池未关闭,该策略直接在调用者线程(main线程)中运行当前被丢弃的任务。显然这样不会真的丢弃任务,但是,调用者线程性能可能急剧下降。

  • DiscardOldestPolicy():
//用拒绝策略----DiscardOldestPolicy()创建线程池
    public static void test5(){
        //自定义线程池。工作中用ThreadPoolExecutor
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()  //队列满了,新来的线程会尝试去和最早的线程竞争
        );

        try {
            //最大承载:maximumPoolSize+ Queue
            for (int i = 0; i < 10; i++) {
                final int temp = i;
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"--->"+temp+"->OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }

结果不一定:队列满了,新来的线程会尝试去和最早的线程竞争,竞争不过就会被抛弃。

  • DiscardPolicy():
//用拒绝策略----DiscardPolicy()创建线程池
    public static void test4(){
        //自定义线程池。工作中用ThreadPoolExecutor
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardPolicy()  //队列满了就会丢掉新来的任务,但是不会抛出异常
        );

        try {
            //最大承载:maximumPoolSize+ Queue
            for (int i = 0; i < 10; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"->OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
**************结果:******************
pool-1-thread-1->OK
pool-1-thread-2->OK
pool-1-thread-2->OK
pool-1-thread-2->OK
pool-1-thread-2->OK
pool-1-thread-3->OK
pool-1-thread-4->OK
pool-1-thread-5->OK

可以发现队列满了后剩余的2个线程被抛弃

小结:

最大线程池大小如何设置(maximumPoolSize ):

  • 1、CPU密集型
    电脑为几核,那么maximumPoolSize 就定义为几。这样可以保证CPU的效率最高
//最大线程如何选择:
    //1、CPU密集型:电脑是几核,那么maximumPoolSize就为几
    public static void test6(){

        //获取CPU的核数
        int processors = Runtime.getRuntime().availableProcessors();
        //自定义线程池。工作中用ThreadPoolExecutor
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                2,
                processors,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()  //队列满了,新来的线程会尝试去和最早的线程竞争
        );

        try {
            //最大承载:maximumPoolSize+ Queue
            for (int i = 0; i < 10; i++) {
                final int temp = i;
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"--->"+temp+"->OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
  • 2、IO密集型
    因为IO十分消耗资源,因此一般先判断程序中十分耗IO的线程有多少(比如为10个);那么设置maximumPoolSize 就要 > 10。一般设置maximumPoolSize 为耗IO线程数量的两倍。避免系统阻塞

相关文章

  • 6、线程池详解

    线程池:3大方法、7大参数、4种拒绝策略 线程池的好处: 降低资源的消耗 提高响应速度 方便统一管理线程复用、可以...

  • JAVA线程池

    线程池详解 ThreadPoolExecutor ThreadPoolExecutor是最灵活的一个线程池,用户可...

  • Java调度线程池ScheduleExecutorService

    作者: 一字马胡 转载标志 【2017-11-03】 更新日志 链接 Java线程池详解(一)Java线程池详解...

  • 3分钟了解 java线程池ThreadPoolExecutor的

    常用的3个线程池 线程池的7个参数详解 1.corePoolSize:线程池中的常驻核心线程数2.maximumP...

  • ThreadPoolExecutor使用详解

    1、ThreadPoolExecutor构造参数的详解 corePoolSize, // 线程池长期维持的线程数,...

  • 线程池详解二:线程池的七大参数及运行流程

    在线程池详解一:线程池概念以及架构[https://www.jianshu.com/p/c03f21033153]...

  • Java 线程池详解

    Java ThreadPoolExecutor详解 ThreadPoolExecutor是Java语言对于线程池的...

  • 线程池详解

    什么时候使用线程池? 单个任务处理时间比较短 需要处理的任务数量很大 使用线程池的好处? 降低资源消耗。 提高响应...

  • 线程池详解

    线程池的使用 1. 为什么使用线程池? 并发的线程数量很多,并且每个执行时间都很短,这样频繁创建线程和销毁...

  • 线程池详解

    1、线程池好处: 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 提高响应速度:当任务到达时...

网友评论

      本文标题:6、线程池详解

      本文链接:https://www.haomeiwen.com/subject/qdekiktx.html