线程池----优势/构造方法/参数解析/参数合理配置/类层级关系/实例分析/线程池正确选择/介绍几种阻塞队列

Table of Contents

一、线程池的优势?

二、四个构造方法

三:线程池参数解释

四、参数合理配置

五、类层级关系

六、创建线程池一个池并分析实例?       

七、线程池的正确选择

八、Java中的几种阻塞队列


一、线程池的优势?

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

二、四个构造方法

ThreadPoolExecutor类是创建线程池的核心类

介绍四种构造方法

 

三:线程池参数解释

corePoolSize 核心线程数大小,队列没满时,只会创建和使用核心线程数大小
maximumPoolSize 非核心线程数(maximumPoolSize-corePoolSize),也叫最大线程数,队列满了之后还有需要入队的产品,就会创建非核心线程来使用,产品出队列给非核心线程工作,需要入队的产品继续入队
keepAliveTime 超时时间大小设置。非核心线程超过这个时间,就会被回收,被视为没有用线程(核心线程默认不会被回收)
unit

设置超时时间单位。有

TimeUnit.DAYS; //天

TimeUnit.HOURS; //小时

TimeUnit.MINUTES; //分钟

TimeUnit.SECONDS; //秒

TimeUnit.MILLISECONDS; //毫秒,常用

TimeUnit.MICROSECONDS; //微妙

TimeUnit.NANOSECONDS; //纳秒

workQueue 线程阻塞队列选择,常用有ArrayBlockingQueue、LinkedBlockingQueue
threadFactroy 创建线程的方式,工厂。可以用默认的,也可以apache和guava等。下面介绍
handler 拒绝策略,当阻塞队列满时,工作线程又已经达到最大数,此时选择采用拒绝策略来应对这种情况,默认是抛出异常这种拒绝策略。
allowCoreThreadTimeOut 控制是否允许核心线程超时退出。设置为true运行核心线程数超时被回收,默认为false
poolSize 线程池中当前线程的数量,当该值为0的时候,意味着没有任何线程,线程池会终止;同一时刻,poolSize不会超过maximumPoolSize

例子说话:

当corePoolSize:5  maximumPoolSize:6 workQueue:5的时候,分析

threadFactroy是一个老板。有一个小仓库(阻塞队列),可以存放5个产品。

场景1:产品到了3个,不往仓库丢,老板直接召了3名职工(corePoolSize)负责完成产品包装。

场景2:产品到了5个,不往仓库丢,老板直接召了5名职工(corePoolSize)负责完成产品包装。

场景3:产品到了6个,往仓库丢一个,老板召了5名职工(corePoolSize)负责完成产品包装。剩下一个等待第一个职工完成后由它来负责

场景4:产品到了11个,往仓库丢5个,老板召了5名职工(corePoolSize)负责完成产品包装。发现还有一个产品没有放下,仓库满了,此时老板用最后的资金又召了一名职工(maximumPoolSize=5+1=6),此时从仓库里拿出一个产品给这个职工包装,还有一个产品放入仓库。

场景5:产品到了12个,往仓库丢5个,老板召了6名职工(maximumPoolSize=6),发现还有一个产品没有放下仓库,仓库已满,职工又全部在工作中,老板也没有资金召职工,此时采用拒绝策略来处理。

场景6:产品到了11个,往仓库丢5个,老板召了5名职工(corePoolSize)负责完成产品包装。发现还有一个产品没有放下,仓库满了,此时老板用最后的资金又召了一名职工(maximumPoolSize=5+1=6),此时从仓库里拿出一个产品给这个职工包装,还有一个产品放入仓库。当时间已经过了一个月(keepAliveTime)时,产品数都是在1-10个之间,最后召了那名职工没事做,此时老板将它开掉,节约薪资。

四、参数合理配置

(1)CPU密集型
       CPU密集型的意思就是该任务需要大量运算,而没有阻塞,CPU一直全速运行。

       CPU密集型任务只有在真正的多核CPU上才可能得到加速(通过多线程)。

       CPU密集型任务配置尽可能少的线程数。

       CPU密集型线程数配置公式:CPU核数+1个线程的线程池

(2)IO密集型
       IO密集型,即该任务需要大量的IO,即大量的阻塞。大量的文件操作。

       在单线程上运行IO密集型任务会导致浪费大量的CPU运算能力浪费在等待。

       所以IO密集型任务中使用多线程可以大大的加速程序运行,即使在单核CPU上,这种加速主要利用了被浪费掉的阻塞时间。

      

       第一种配置方式:

       由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程。

       配置公式:CPU核数 * 2 + 1。

       第二种配置方式:

       IO密集型时,大部分线程都阻塞,故需要多配置线程数。

       配置公式:CPU核数 / 1 – 阻塞系数(0.8~0.9之间)

       比如:8核 / (1 – 0.9) = 80个线程数

五、类层级关系

六、创建线程池一个池并分析实例?       

ExecutorService executorService = new ThreadPoolExecutor(5, 6, 10L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory());

        for (int i = 0; i < 11; i++) {
            executorService1.execute(new Thread() {
                @Override
                public void run() {
                    System.out.println("aaa");
                    try {
                        sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        Thread.sleep(200);

        System.out.println( ((ThreadPoolExecutor) executorService).getActiveCount());
        executorService.shutdown();

代码解析:创建5个corePool核心线程、1个非核心线程(maximumPoolSize=1+5=6)、5个ArrayBlockingQueue<>有界队列,非核心线程过时策略是10毫秒被回收,采用默认工厂场景线程池。

当有11个任务到来时(这里11个线程理解成11个产品或任务),执行情况分析如下:

运行结果(输出6个活跃线程,说明非核心线程也参与了工作)

当有12个产品/线程到来时,启动默认拒绝策略,抛出异常

当有10个产品时,不会使用非核心线程数,因为队列刚好放的下核心线程未处理剩下的产品

七、线程池的正确选择

由Executors类来创建线程池默认不推荐,可能会造成oom异常,创建方式很多,如下

但是这些场景方式,默认的阻塞队列长度是Integer.MAX_VALUE,就是2^32-1

阿里巴巴都不推荐使用!!!

创建线程池的正确方式:

避免使用Executors创建线程池,主要是避免使用其中的默认实现,那么我们可以自己直接调用ThreadPoolExecutor的构造函数来自己创建线程池。在创建的同时,给BlockQueue指定容量就可以了,也可以通过设置自己的工厂来创建线程。

创建线程的工厂都由自己指定,使用apache和guava等,这里没有他们的maven依赖,搞不定。。


八、Java中的几种阻塞队列

一般使用ArrayBlockingQueueLinkedBlockingQueue设置队列大小

参考博客:https://blog.csdn.net/gaotiedun1/article/details/86606135

猜你喜欢

转载自blog.csdn.net/qq_41055045/article/details/102646589