JavaGuide知识点整理——线程池的最佳实践

源代码 2024-9-26 04:36:44 75 0 来自 中国
重新声明下,固然开这系列条记的时间就说了这是迩来看javaguide网站,然后为了加深影象也为了好知识一起分享,以是把网站中的知识点搬运了一遍,此中掺杂这我自己的明白和实践等。然后各位如果感觉去可以去看原文。附上链接:https://snailclimb.gitee.io/javaguide/#/./docs/java/concurrent/java-thread-pool-best-practices
线程池在现实项目中的利用场景

线程池一样寻常用于实验多个不干系联的耗时任务。没有多线程的情况下,任务利用序次实验,利用了线程池的话可以让多个不干系联的任务同时实验。
假设我们要实验三个不干系的耗时任务。利用线程池前后的区别如下:

注意这里利用多线程实验差别的任务,可以用一个countDownLatch等候子任务实验完成才继承往下返回结果。
线程池最佳实践

利用ThreadPoolExecutor的构造函数声明线程池

线程池必须手动通过ThreadPoolExecutor的构造函数声明,克制利用Executors类的newFixedThreadPool和newCachedThreadPool,由于大概会有oom的风险。
说白了就是利用有界队列,控制线程创建数量。
除了克制OOM的缘故因由外,不保举利用Executors提佛谁人的快捷线程池另有两个缘故因由:

  • 现实利用中须要根据自己呆板的性能,业务场景来手动设置线程池的参数。好比焦点线程数,最大线程数,利用的任务队列,拒绝计谋等。
  • 我们应该表现的给我们的线程池命名,如许有助于我们定位标题。
监测线程池运行状态

我们可以通过一些手段来检测线程池的运行状态,好比SpringBoot中的Actuator组件。
除此之外我们还可以通过ThreadPoolExecutor的干系api做一个大略的监控。从下图可以看出,ThreadPoolExecutor提供了互殴线程池当前的线程数和活泼线程数,已实验完成的任务数,正在列队的任务数等任务。

发起差别类别的业务用差别的线程池

许多人在现实项目中会有类似如许的标题:我的项目中多个业务须要用到线程池,是为每个线程池都界说一个照旧说界说一个公共的线程池呢?
一样寻常发起差别的业务利用差别的线程池, 设置线程池的时间根据当前业务情况对当火线程池举行设置,由于差别的业务的并发以及对资源的利用情况都差别,重新优化体系性能瓶颈干系的业务。
下面我们看一个线程池运用不当的线上事故案例:

图中的代码大概会存在死锁的情况。为什么呢?下面我们捋一捋。
试想这么一个非常征象:如果焦点线程数是n,父任务数量也为n。把焦点线程全部占用。然后父任务下的子任务也须要用线程。在任务队列中阻塞等候获取线程。而父任务在等候子任务实验完成,子任务等候父任务开释线程资源好获取线程。也就造成了死锁。
办理方法也很简单,新增长一个用于实验子任务的线程池专门为其服务。
要给线程池命名

初始化线程池的时间须要表现命名(设置线程池名称前缀),有利于定位标题。
默认情况下创建的线程名字类似pool-1-thread-n如许的,没有业务寄义,倒霉于我们定位标题。
给线程池里的线程命名通常有两种方式:

  • 利用谷歌的ThreadFactoryBuilder给线程池里的线程命名
    public static void main(String[] args) throws Exception {        ThreadFactory threadFactory =            new ThreadFactoryBuilder().setNameFormat("用来测似的线程池" + "-%d").setDaemon(true).build();       ThreadPoolExecutor threadPoolExecutor =           new ThreadPoolExecutor(2,10,1l,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10),threadFactory);       for(int i = 0;i<10;i++){           threadPoolExecutor.execute(()->{               System.out.println("当火线程名:"+Thread.currentThread().getName());           });       }    }如上代码就是命名了,下面是如果这个线程池里的线程报错,可以很轻易定位。

4.png

  • 也可以自己实现ThreadFactor来给线程池里的线程命名
public class Test {    public static void main(String[] args) throws Exception {        MyThreadFactory threadFactory = new MyThreadFactory(Executors.defaultThreadFactory(),"测试线程池");       ThreadPoolExecutor threadPoolExecutor =           new ThreadPoolExecutor(2,10,1l,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10),threadFactory);       for(int i = 0;i<10;i++){           threadPoolExecutor.execute(()->{               System.out.println("当火线程名:"+Thread.currentThread().getName());           });       }    }}final  class MyThreadFactory implements ThreadFactory{    ThreadFactory threadFactory;    String name;    AtomicInteger i = new AtomicInteger(1);    MyThreadFactory(ThreadFactory threadFactory,String name){        this.threadFactory = threadFactory;        this.name = name;    }    @Override    public Thread newThread(Runnable r) {        Thread t = threadFactory.newThread(r);        t.setName(name+i.getAndIncrement());        return t;    }}实在这种写法就是单纯的包了一层,每一个线程都手动的setName给设置了个名字。具体要用那种写法都可以,反正我是以为自己设置的这个最开始写要贫苦点,但是每次用方便。谷歌的方法不须要创建什么工具类,但是每次创建都要设置。
正确的设置线程池参数

通例操纵
起首这里要说一个常识:并不是线程越多越好。好比一个很小的任务,一个人做要1小时,但是60个人也不会是一分钟。以致由于人多交换本钱太大。指不定还会用时更长。
线程数量过多的影响也是和我们分配多少人服务一样。对于多线程的场景重要是增长了上下文切换本钱。
类比我们人类通过相助做某件事,我们可以知道线程池过大过小都欠好,符合才是最好的。
如果我们设置的线程池数量太小,同一时间有大量任务/哀求须要处置惩罚,大概会导致大量的哀求/任务在任务队列中列队等候实验,以致出现队列满了之后任务/哀求无法处置惩罚的情况。大概大量任务堆积在队列中导致OOM,如许很显着是有标题的。CPU根本没有得到充实利用。
但是我们设置线程数量过大,大量线程大概同时争取CPU资源,如许会导致大量的上下文切换,从而增长线程的实验时间,影响团体实验服从。
网上有一个简单而且通用的公式:

  • CPU麋集型任务(n+1):这种任务斲丧的是CPU资源,可以将线程数设置为cpu焦点数+1.比CPU焦点数多一个是为了防止线程偶发的缺页克制大概其他缘故因由导致的任务停息。一旦任务停息,cpu就会处于空闲状态,这个时间多出的一个线程就可以充实利用CPU时间。
  • I/O麋集型任务(2n):这种任务应用起来体系会占用大部门时间处置惩罚I/O交互,而线程处置惩罚I/O的时间段不会占用CPU来处置惩罚,这时间就可以把CPU交出给其他线程利用。以是我们可以多设置一些线程。好比2N。
怎样判定是CPU麋集任务照旧IO麋集任务?
CPU麋集型简单明白就是利用CPU盘算本领的任务。好比在内存中对大量数据举行排序。但凡涉及到网络读取,文件读取这类都是IO麋集型。这类任务的特点是CPU盘算斲丧时间相比于等候IO操纵完成的时间来说很少。大部门时间都淹灭在等候IO操纵完成上。
美团的骚操纵

美团技能团队在java线程池实现原理以及在内图案业务中的实践这篇文章中先容到对线程池参数实现可自界说设置的思绪和方法。
美团技能团队的思绪是重要对线程池的焦点参数实现自界说可设置。这三个焦点参数是:

  • corPoolSize:焦点线程数界说了最小可以同时运行的线程数量。
  • maximumPoolSize:当队列中存放的任务到达队列容量时,当前可以同事运行的线程数量变为最大线程数。
  • workQueue:当新任务来的时间会先判定当前运行的线程数量是否到达焦点线程数,如果到达的话新任务会被存放在队列中。
  • 还包罗一些队列长度等。

实现的重点是基于ThreadPoolExecutor的几个方法,我们只须要维护ThreadPoolExecutor的实例,并在须要修改的时间拿到实例修改其参数即可。基于这个原理我们做到线程池参数的动态化,可视而且可设置。结果如下图。

实在我们基于这个头脑可以做的就更多了。好比一些监控:线程池活泼度,告警,实验任务的频率和耗时,Reject非常等等。从而克制故障大概加快故障的修复。感觉美团技能团队对于线程池的实践先容比力浅近易懂,感爱好的可以自己去看下。
本篇条记就记到这里,如果稍微帮到你了记得点个喜好点个关注。也祝各人工作顺顺遂利,生存健康健康~!
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 18:24, Processed in 0.183573 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表