阿里为什么不建议使用Executors创建线程池
满怀忧思,不如先干再说!做干净纯粹的技术分享,赞想点就点,注想关就关,有话评论区直接走起来!
2023祝您【兔】飞猛进,新年快乐,这篇是年前最后一篇,本章收录于《Java并发编程》,讲解通过Executors创建线程池来执行任务,当然在阿里Java开发手册中是不推荐使用这种方式的,我们也最好使用并且思考一下为什么不推荐使用这种方式。接下来跟随【添甄】使用【Executors】,到最后相信你就知道为什么不推荐使用这种方式,通过本篇你可以获得:Executors创建线程六种方式和适用场景;通过源码分析揭秘Executors创建线程原理;自定义工厂,给线程起个好名字;Executors和ThreadPoolExecutor区别。Executors创建线程六种方式
Executors构造方法私有,提供了六种创建线程的静态方法:
方法
说明
Executors。newFixedThreadPool
固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待;
Executors。newCachedThreadPool
可缓存的线程池,若线程数超过需求数,缓存一段时间后回收,若线程数不够,则新建线程;
Executors。newSingleThreadExecutor
单个线程数的线程池,它可以保证先进先出的执行顺序;
Executors。newScheduledThreadPool
可以执行延迟任务的线程池;
Executors。newSingleThreadScheduledExecutor
单线程的可以执行延迟任务的线程池;
Executors。newWorkStealingPool
抢占式执行的线程池(任务执行顺序不确定)【JDK1。8添加】newFixedThreadPool
特点:固定线程数量,即最大线程数核心线程数,没有临时线程【救急线程】,无需超时时间,而且阻塞队列是无界队列,可以存放任意数量的任务两个重载方法指定线程池大小newFixedThreadPool(intnThreads)指定线程池大小,并且指定工厂newFixedThreadPool(intnThreads,ThreadFactorythreadFactory)
发现调用ThreadPoolExecutor创建的线程池
应用案例importjava。util。concurrent。ExecutorService;importjava。util。concurrent。Executors;publicclassExecutorsMain{publicstaticvoidmain(String〔〕args){1、创建工作线程数为3的线程池,并使用默认工厂ExecutorServicefixedThreadPoolExecutors。newFixedThreadPool(3);2、提交6个任务,多余的任务会进入队列for(inti0;i6;i){finalintindexi;3、执行任务fixedThreadPool。execute((){try{休眠3秒Thread。sleep(3000);}catch(InterruptedExceptione){e。printStackTrace();}运行时间StringstartTimeLocalDateTime。now()。format(DateTimeFormatter。ofPattern(HH:mm:ssSSS));System。out。println(startTimeThread。currentThread()。getName()index:index);});}关闭线程池后,已提交的任务仍然会执行完fixedThreadPool。shutdown();}}
运行发现,前3个任务和后3个任务相差3秒启动,可同时执行3个任务
自定义线程工厂
线程工厂的主要作用其实就是给线程起一个有意义的名字publicclassExecutorsMain{publicstaticvoidmain(String〔〕args){创建工作线程数为3的线程池,并自定义线程工厂,指定线程名字ExecutorServicefixedThreadPoolExecutors。newFixedThreadPool(3,newThreadFactory(){定义序号privateAtomicIntegertnewAtomicInteger(1);OverridepublicThreadnewThread(NotNullRunnabler){我的线程都是以tianzhen开头执行次数returnnewThread(r,tianzhent。getAndIncrement());}});。。。。。。}}ThreadFactory接口只有一个抽象方法也可以使用lambda创建,如下ExecutorServicefixedThreadPoolExecutors。newFixedThreadPool(3,r{AtomicIntegertnewAtomicInteger(1);returnnewThread(r,tianzhent。getAndIncrement());});
运行结果:
适用场景
适用于任务量已知的场景,如果任务量过多,队列无限膨胀可能引发OOMnewCachedThreadPool
特点:核心线程数为0,最大线程数为Integer。MAXVALUE,临时线程存活时间为60秒,言下之意就是所有线程都是临时线程,空闲60秒回收,而且数量理论上可以无限创建,直到Integer。MAXVALUE个,讲道理创建Integer。MAXVALUE个线程JVM内存早就爆了队列采用SynchronousQueue,没有容量,相当于一个通道,没有线程执行任务,任务就放不进去
两个重载:使用默认工厂newCachedThreadPool()自定义工厂newCachedThreadPool(ThreadFactorythreadFactory)应用案例publicclassExecutorsMain{publicstaticvoidmain(String〔〕args){创建缓存线程池ExecutorServicecachedThreadPoolExecutors。newCachedThreadPool();提交6个任务for(inti0;i6;i){finalintindexi;cachedThreadPool。execute((){try{Thread。sleep(2000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}StringstartTimeLocalDateTime。now()。format(DateTimeFormatter。ofPattern(HH:mm:ssSSS));System。out。println(startTimeThread。currentThread()。getName()index:index);});}关闭线程池后,已提交的任务仍然会执行完cachedThreadPool。shutdown();}}
发现所有的任务都是同一时间开始执行,而且线程数与任务数相同
适用场景
适用于任务密集,但是每个任务执行时间较短的场景,可以快速处理任务newSingleThreadExecutor
从源码中发现,此处使用【装饰模式】,将ThreadPoolExecutor装饰为FinalizableDelegatedExecutorService对外暴露ExecutorService,保障不能调用ThreadPoolExecutor中的setCorePoolSize方法改变线程池大小特点线程数固定为1任务数量超过1时,会放入无界队列即时任务执行完毕,线程也不会被释放应用案例
创建单线程线程池,执行三个任务,其中第二个任务会出现异常publicclassExecutorsMain{publicstaticvoidmain(String〔〕args){创建单线程线程池ExecutorServicesingleThreadExecutorExecutors。newSingleThreadExecutor();正常执行singleThreadExecutor。execute((){StringstartTimeLocalDateTime。now()。format(DateTimeFormatter。ofPattern(HH:mm:ssSSS));System。out。println(startTimeThread。currentThread()。getName()启动1);});抛出异常singleThreadExecutor。execute((){StringstartTimeLocalDateTime。now()。format(DateTimeFormatter。ofPattern(HH:mm:ssSSS));数学异常inti100;System。out。println(startTimeThread。currentThread()。getName()启动2);});正常执行singleThreadExecutor。execute((){StringstartTimeLocalDateTime。now()。format(DateTimeFormatter。ofPattern(HH:mm:ssSSS));System。out。println(startTimeThread。currentThread()。getName()启动3);});关闭线程池后,已提交的任务仍然会执行完singleThreadExecutor。shutdown();}}
运行结果:发现任务出错,会创建新的线程继续执行其他任务
面试题问:单线程线程池和直接newThread串行执行任务有什么区别?
自己创建单线程串行执行任务,如果任务失败而终止没有任何的补救措施,需要在catch中做处理,而线程池会再创建新线程,保障线程池正常工作适用场景
希望任务排队执行时可以使用newScheduledThreadPool
调用newScheduledThreadPool方法内部创建ScheduledThreadPoolExecutor对象,该类的构造中,调用了器父类的构造方法,ScheduledThreadPoolExecutor的父类就是ThreadPoolExecutor
继承关系:
特点
创建定长线程池,可执行周期性的任务
两个重载:固定长度线程池newScheduledThreadPool(intcorePoolSize)定义工厂newScheduledThreadPool(intcorePoolSize,ThreadFactorythreadFactory)应用案例
每隔3秒任务就执行一次,每个任务需要2秒时间执行完毕publicclassExecutorsMain{publicstaticvoidmain(String〔〕args){创建定时线程池ScheduledExecutorServicescheduledThreadPoolExecutors。newScheduledThreadPool(3);启动三个任务for(inti0;i3;i){finalintindexi;scheduleWithFixedDelay固定的延迟时间执行任务;scheduleAtFixedRate固定的频率执行任务此处就是3秒执行一次scheduledThreadPool。scheduleWithFixedDelay((){try{Thread。sleep(2000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}StringstartTimeLocalDateTime。now()。format(DateTimeFormatter。ofPattern(HH:mm:ssSSS));System。out。println(startTimeThread。currentThread()。getName()启动index);},0,3,TimeUnit。SECONDS);}}}
运行结果:此处发现每个任务都是通过对应的线程同时执行,而且任务间隔时间为5秒,执行需要2秒3秒的周期得来的
适用场景
适用于多任务,并且这些任务需要周期性执行的场景,比如每天计算一次粉丝增长数量,阅读数量,点赞数量,我们可以开启三个线程,24小时计算一次newSingleThreadScheduledExecutor
发现也是使用了装饰模式,封装成DelegatedScheduledExecutorService对象返回,内部调用ScheduledThreadPoolExecutor,自然和上一个线程池一样,最终还是通过ThreadPoolExecutor创建的线程池特点
创建单线程可执行周期性任务的线程池,其实就是SingleThread和ScheduledThread的结合体应用案例
3秒为周期执行任务,并且每个任务需要2秒时间完成publicclassExecutorsMain{publicstaticvoidmain(String〔〕args){创建周期运行单线程线程池ScheduledExecutorServicesingleScheduledThreadPoolExecutors。newSingleThreadScheduledExecutor();提交3个固定频率执行的任务for(inti0;i3;i){finalintindexi;singleScheduledThreadPool。scheduleAtFixedRate((){try{Thread。sleep(2000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}StringstartTimeLocalDateTime。now()。format(DateTimeFormatter。ofPattern(HH:mm:ssSSS));System。out。println(startTimeThread。currentThread()。getName()启动index);},0,3,TimeUnit。SECONDS);}}}
运行结果:每个任务都是同一个线程执行的请评论区说出间隔时间为什么为6秒?
适用场景
适用于需要排队执行的周期性任务newWorkStealingPool
通过ForkJoinPool创建线程池,该类继承AbstractExecutorService抽象类publicclassForkJoinPoolextendsAbstractExecutorService特点在线程空闲时可以执行其他线程来不及处理的任务重载方法无参,不设置任务并行数量,默认为cpu数newWorkStealingPool()设置任务并行数量newWorkStealingPool(intparallelism)应用案例publicclassExecutorsMain{publicstaticvoidmain(String〔〕args){创建4个工作线程的线程池,如果不设置并行数,默认取CPU总核数ExecutorServiceworkStealingThreadPoolExecutors。newWorkStealingPool(4);for(inti0;i10;i){finalintindexi;workStealingThreadPool。execute((){try{模拟任务执行时间为任务编号为012的执行时间需要3秒;其余任务200毫秒,导致任务时间差异较大if(index2){Thread。sleep(3000);}else{Thread。sleep(200);}}catch(InterruptedExceptione){e。printStackTrace();}System。out。println(Thread。currentThread()。getName()index:index);});}}}
执行结果:
可以看出ForkJoinPool1worker0线程将39号任务都执行了,就是这些任务执行的比较快,另外三个线程执行任务比较慢,这些任务可能也分配给了另外三个线程执行,但是另外三个线程还在忙,它在空闲的时候就把别的线程没有执行的任务偷过来执行了
适用场景
使用在不需要保证执行顺序,任务耗时差异较大的场景,可以利用空闲线程快速执行任务Executors和ThreadPoolExecutor区别Executors类和ThreadPoolExecutor都是util。concurrent并发包下面的类Executos下面的newFixedThreadPool、newScheduledThreadPool、newSingleThreadExecutor、newCachedThreadPool底线都是通过ThreadPoolExecutor实现使用ThreadPoolExecutor我们可以传入更多参数,更加灵活
Executos是创建固定的线程池,对于项目不同业务的实际情况,这些固定模板的线程池多数情况下不符合是业务需求,所以不推荐使用,使用ThreadPoolExecutor根据具体问题,具体设置参数
婚礼现场新娘身穿洛丽塔亮相,颜值气质惊艳众人!网友梦中情裙婚礼对于女孩子们来说是一件美好又难忘的事情,相信这天大家都会穿上心目中梦寐以求的婚服,在亲朋好友面前闪亮登场,展现出自己最光鲜最漂亮的一面,然而并不是所有女生结婚都会穿婚纱打扮自己
末代皇后真实照片气质碾压女星,婉容和南芳各有千秋王朝走到末尾之时,后宫的女性们无疑是悲惨的,此时不管如何的花容月貌,终究被历史的洪流裹挟着,难言幸福清朝的末代皇后,最后一位享受到皇后礼仪的人,就是婉容,溥仪,以皇后的礼仪迎娶了末
娱乐圈5位痞帅男明星,气质长相个个都能迷死人近些年,娱乐圈特别流行一个形容男人很帅的词语痞帅,那么痞帅到底是什么意思呢?其实,痞帅更多是形容男人内在的帅气。痞帅样貌虽然不一定精致,但一定是要坏的恰到好处,能用俏皮话撩妹,还不
从陈情令到少年歌行,整容式出圈,曹煜辰由此大火江湖门派,少年历险,所求正道,这三个元素向来是古装武侠剧里的核心。比起腻歪虐心的偶像剧,近年来观众格外偏爱以升级打怪为主线的青春群像剧。从大宋少年志第二季成本增加,投资翻倍就能看出
偶遇陈飞宇是什么体验?身穿皮衣在逛街,明星气质压根挡不住一说起陈飞宇,这位长相帅气气质出众的男演员似乎从出道开始就备受关注。陈飞宇的爸爸是大导演陈凯歌,妈妈是琼瑶女郎陈红,有着完善的资源加持和完美的外在形象,让这位内娱太子爷一直都很有人
外来媳妇本地郎46岁湛江美女伍燕,已嫁富商生子,生活幸福!在广东影视剧里,有一部经典剧,该剧用了二十三年的时间,打造出了一个属于广东文化特色的文化剧。该剧就是家喻户晓的外来媳妇本地郎。早已突破四千集的外来媳妇本地郎,面临着一个很严峻的问题
张天爱金发皮衣大片霸气酷飒穿超短裤黑丝秀美腿妖娆妩媚性感撩人娱兔迎春张天爱瑞丽服饰美容二月刊封面大片曝光,金发搭配皮衣酷飒又帅气。廓形皮衣简约利落,金发造型温柔大气。凌乱飞舞的金色发丝飘逸不羁,张天爱的眼神犀利又魅惑。金发皮衣搭配超短裙再穿
烦恼挂碍究竟怎么生出来的呢?如果我是个聋子,你夸我跟骂我本无差别,如果我是个瞎子,我娶西施跟娶东施做老婆本无差别,如果我没了嗅觉,我身在茅厕或者是花海本无差别。如果我死了,我的尸体是让狗吃了,还是风光大葬本无
再勇敢就不礼貌了阅读本文前,点击上方卡片一键关注Episode02440在这个不是很有人情味的世界里有人对你好的确挺值得骄傲的能够时时刻刻保持觉知的生活,本身就是答案。春节意义的就是让长大的我们做
春节回乡手记近乡情更切文朱小平(湖南)年关晴空,在疾驰的车窗外,显得异常温煦而响亮。它照着湖乡奔流的潺潺小溪,波光粼耀,似有鱼翔浅底它照着平阔的田野与齐整的新农村楼舍,移步不换熟谙的景致。我到了我回到了
2023年真的很关键!囗2023年,对谁都特别关键,经过三年的蛰伏与思考,人心思变思动是一种必须与必然。囗疫情的危害是全面的,每一个人都感同身受,但再也不能成为搪塞的借口和推脱的理由。囗躺平是一种无奈选