• 创建自定义线程池(最大线程数该如何设置?)


    CPU密集型      IO密集型

    一:CPU密集型

      定义:CPU密集型也是指计算密集型,大部分时间用来做计算逻辑判断等CPU动作的程序称为CPU密集型任务。该类型的任务需要进行大量的计算,主要消耗CPU资源。  这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。

      特点:

         01:CPU 使用率较高(也就是经常计算一些复杂的运算,逻辑处理等情况)非常多的情况下使用

         02:针对单台机器,最大线程数一般只需要设置为CPU核心数的线程个数就可以了

            03:这一类型多出现在开发中的一些业务复杂计算和逻辑处理过程中。

      代码示例:

     1 package pool;
     2 
     3 import java.util.concurrent.Executors;
     4 import java.util.concurrent.LinkedBlockingDeque;
     5 import java.util.concurrent.ThreadPoolExecutor;
     6 import java.util.concurrent.TimeUnit;
     7 
     8 public class Demo02 {
     9     public static void main(String[] args) {
    10         //自定义线程池! 工作中只会使用 ThreadPoolExecutor
    11 
    12         /**
    13          * 最大线程该如何定义(线程池的最大的大小如何设置!)
    14          * 1、CPU  密集型,几核,就是几,可以保持CPU的效率最高!
    15          */
    16 
    17         //获取电脑CPU核数
    18         System.out.println(Runtime.getRuntime().availableProcessors());    //8核
    19 
    20         ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
    21                 2,                                        //核心线程池大小
    22                 Runtime.getRuntime().availableProcessors(),   //最大核心线程池大小(CPU密集型,根据CPU核数设置)
    23                 3,                                       //超时了没有人调用就会释放
    24                 TimeUnit.SECONDS,                             //超时单位
    25                 new LinkedBlockingDeque<>(3),                 //阻塞队列
    26                 Executors.defaultThreadFactory(),             //线程工厂,创建线程的,一般不用动
    27                 new ThreadPoolExecutor.AbortPolicy());        //银行满了,还有人进来,不处理这个人的,抛出异常
    28 
    29         try {
    30             //最大承载数,Deque + Max    (队列线程数+最大线程数)
    31             //超出 抛出 RejectedExecutionException 异常
    32             for (int i = 1; i <= 9; i++) {
    33                 //使用了线程池之后,使用线程池来创建线程
    34                 threadPool.execute(()->{
    35                     System.out.println(Thread.currentThread().getName()+" ok");
    36                 });
    37             }
    38         } catch (Exception e) {
    39             e.printStackTrace();
    40         } finally {
    41             //线程池用完,程序结束,关闭线程池
    42             threadPool.shutdown();      //(为确保关闭,将关闭方法放入到finally中)
    43         }
    44     }
    45 }

    二:IO密集型:

      定义:IO密集型任务指任务需要执行大量的IO操作,涉及到网络、磁盘IO操作,对CPU消耗较少,其消耗的主要资源为IO。

        我们所接触到的 IO ,大致可以分成两种:磁盘 IO和网络 IO。

            01:磁盘 IO ,大多都是一些针对磁盘的读写操作,最常见的就是文件的读写,假如你的数据库、 Redis 也是在本地的话,那么这个也属于磁盘 IO。

            02:网络 IO ,这个应该是大家更加熟悉的,我们会遇到各种网络请求,比如 http 请求、远程数据库读写、远程 Redis 读写等等。

           IO 操作的特点就是需要等待,我们请求一些数据,由对方将数据写入缓冲区,在这段时间中,需要读取数据的线程根本无事可做,因此可以把 CPU 时间片让出去,直到缓冲区写满。

    既然这样,IO 密集型任务其实就有很大的优化空间了(毕竟存在等待):

           CPU 使用率较低,程序中会存在大量的 I/O 操作占用时间,导致线程空余时间很多,所以通常就需要开CPU核心数两倍的线程。当线程进行 I/O 操作 CPU 空闲时,线程等待时间所占比例越高,就需要越多线程,启用其他线程继续使用 CPU,以此提高 CPU 的使用率;线程 CPU 时间所占比例越高,需要越少的线程,这一类型在开发中主要出现在一些计算业务频繁的逻辑中。

      代码示例:

     1 package pool;
     2 
     3 import java.util.concurrent.Executors;
     4 import java.util.concurrent.LinkedBlockingDeque;
     5 import java.util.concurrent.ThreadPoolExecutor;
     6 import java.util.concurrent.TimeUnit;
     7 
     8 public class Demo02 {
     9     public static void main(String[] args) {
    10         //自定义线程池! 工作中只会使用 ThreadPoolExecutor
    11 
    12         /**
    13          * 最大线程该如何定义(线程池的最大的大小如何设置!)
    14          * 2、IO   密集型  >判断你程序中十分耗IO的线程
    15          *      程序    15个大型任务   io十分占用资源!  (最大线程数设置为30)
    16          *      设置最大线程数为十分耗io资源线程个数的2倍
    17          */
    18 
    19         //获取电脑CPU核数
    20         System.out.println(Runtime.getRuntime().availableProcessors());   //8核
    21 
    22         ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
    23                 2,                               //核心线程池大小
    24                 16,                     //若一个IO密集型程序有15个大型任务且其io十分占用资源!(最大线程数设置为 2*CPU 数目)
    25                 3,                                //超时了没有人调用就会释放
    26                 TimeUnit.SECONDS,                 //超时单位
    27                 new LinkedBlockingDeque<>(3),     //阻塞队列
    28                 Executors.defaultThreadFactory(),               //线程工厂,创建线程的,一般不用动
    29                 new ThreadPoolExecutor.DiscardOldestPolicy());  //队列满了,尝试和最早的竞争,也不会抛出异常
    30 
    31         try {
    32             //最大承载数,Deque + Max    (队列线程数+最大线程数)
    33             //超出 抛出 RejectedExecutionException 异常
    34             for (int i = 1; i <= 9; i++) {
    35                 //使用了线程池之后,使用线程池来创建线程
    36                 threadPool.execute(()->{
    37                     System.out.println(Thread.currentThread().getName()+" ok");
    38                 });
    39             }
    40         } catch (Exception e) {
    41             e.printStackTrace();
    42         } finally {
    43             //线程池用完,程序结束,关闭线程池
    44             threadPool.shutdown();      //(为确保关闭,将关闭方法放入到finally中)
    45         }
    46     }
    47 }

    接下来我们进行一一分析:

    1:高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换

    2:并发不高、任务执行时间长的业务这就需要区分开看了:

      a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以适当加大线程池中的线程数目,让CPU处理更多的业务

      b)假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,线程池中的线程数设置得少一些,减少线程上下文的切换

    (其实从一二可以看出无论并发高不高,对于业务中是否是cpu密集还是I/O密集的判断都是需要的当前前提是你需要优化性能的前提下)

    3:并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,我们的项目使用的时redis作为缓存(这类非关系型数据库还是挺好的)。增加服务器是第二步(一般政府项目的首先,因为不用对项目技术做大改动,求一个稳,但前提是资金充足),至于线程池的设置,设置参考 2 。最后,业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件(任务时间过长的可以考虑拆分逻辑放入队列等操作)对任务进行拆分和解耦。

    三.:总结:

      01:一个计算为主的程序(CPU密集型程序),多线程跑的时候,可以充分利用起所有的 CPU 核心数,比如说 8 个核心的CPU ,开8 个线程的时候,可以同时跑 8 个线程的运算任务,此时是最大效率。但是如果线程远远超出 CPU 核心数量,反而会使得任务效率下降,因为频繁的切换线程也是要消耗时间的。因此对于 CPU 密集型的任务来说,线程数等于 CPU 数是最好的了。

      02:如果是一个磁盘或网络为主的程序(IO密集型程序),一个线程处在 IO 等待的时候,另一个线程还可以在 CPU 里面跑,有时候 CPU 闲着没事干,所有的线程都在等着 IO,这时候他们就是同时的了,而单线程的话此时还是在一个一个等待的。我们都知道 IO 的速度比起 CPU 来是很慢的。此时线程数等于CPU核心数的两倍是最佳的。

  • 相关阅读:
    Java 中几种常用的线程池
    阿里巴巴java工程师面试经验详情
    设计模式-备忘录模式
    设计模式-职责链模式
    设计模式-中介者模式
    设计模式-解释器模式
    设计模式-观察者模式
    设计模式-迭代器模式
    设计模式-命令模式
    设计模式-模板方法模式
  • 原文地址:https://www.cnblogs.com/liangbaolong/p/13201403.html
Copyright © 2020-2023  润新知