1.nacosåç
2.Dubbo调用超时那些事儿
3.å¸¦ä½ å¦ä¼åºåScheduledThreadPoolExecutorä¸Timer
4.线程池调优之动态参数配置
nacosåç
nacosç®åæ¯éæå°spring cloud alibabaéå»çï¼ä¹å°±æ¯å¨spring cloudçæ åä¹ä¸å®ç°äºä¸äºä¸è¥¿ï¼spring cloudèªå·±æ¯æä¸ä¸ªæ¥å£ï¼å«åServiceRegistryï¼ä¹å°±æ¯æå¡æ³¨åä¸å¿çæ¦å¿µï¼nacosä¸æä¸ä¸ªå®çå®ç°ç±»NacosServiceRegistryï¼å®ç°äºregisterãderegisterãcloseãsetStatusãgetStatusä¹ç±»çæ¹æ³ã
èªå¨è£ é æ¯ä¸ä¸ªspring bootçä¸ä¸ªæ¦å¿µï¼èªå¨è£ é çææï¼å ¶å®å°±æ¯è¯´ç³»ç»å¯å¨çæ¶åï¼èªå¨è£ é æºå¶ä¼è¿è¡ï¼å®ç°ä¸äºç³»ç»çåå§åï¼èªå¨è¿è¡ï¼ä¹å°±æ¯ç³»ç»å¯å¨æ¶èªå¨å»è°ç¨NacosServiceRegistryçregisteræ¹æ³å»è¿è¡æå¡æ³¨åãèä¸é¤äºæ³¨åä¹å¤ï¼è¿ä¼éè¿schedule线ç¨æ± å»æ交ä¸ä¸ªå®æ¶è°åº¦ä»»å¡ï¼æºç å¦ä¸ï¼
this.exeutorService.schedule(new BeatReactor.BeatTask(beatInfo),源码 beatInfo.getPeriod(), TimeUnit.MILLISECONDS)ï¼è¿å°±æ¯ä¸ä¸ªå¿è·³æºå¶ï¼å®æ¶åéå¿è·³ç»nacos serverã
ç¶åä¼è®¿é®nacos serverçopen apiï¼å ¶å®å°±æ¯/post/
å¸¦ä½ å¦ä¼åºåScheduledThreadPoolExecutorä¸Timer
æè¦ï¼æ¬æç®åä»ç»ä¸ScheduledThreadPoolExecutorç±»ä¸Timerç±»çåºå«ï¼ScheduledThreadPoolExecutorç±»ç¸æ¯äºTimerç±»æ¥è¯´ï¼ç©¶ç«æåªäºä¼å¿ï¼ä»¥åäºè åå«å®ç°ä»»å¡è°åº¦çç®å示ä¾ãJDK1.5å¼å§æä¾ScheduledThreadPoolExecutorç±»ï¼ScheduledThreadPoolExecutor类继æ¿ThreadPoolExecutorç±»éç¨çº¿ç¨æ± å®ç°äºä»»å¡çå¨ææ§è°åº¦åè½ãå¨JDK1.5ä¹åï¼å®ç°ä»»å¡çå¨ææ§è°åº¦ä¸»è¦ä½¿ç¨çæ¯Timerç±»åTimerTaskç±»ãæ¬æï¼å°±ç®åä»ç»ä¸ScheduledThreadPoolExecutorç±»ä¸Timerç±»çåºå«ï¼ScheduledThreadPoolExecutorç±»ç¸æ¯äºTimerç±»æ¥è¯´ï¼ç©¶ç«æåªäºä¼å¿ï¼ä»¥åäºè åå«å®ç°ä»»å¡è°åº¦çç®å示ä¾ã
äºè çåºå«çº¿ç¨è§åº¦Timeræ¯å线ç¨æ¨¡å¼ï¼å¦ææ个TimerTaskä»»å¡çæ§è¡æ¶é´æ¯è¾ä¹ ï¼ä¼å½±åå°å ¶ä»ä»»å¡çè°åº¦æ§è¡ã
ScheduledThreadPoolExecutoræ¯å¤çº¿ç¨æ¨¡å¼ï¼å¹¶ä¸éç¨çº¿ç¨æ± ï¼æ个ScheduledFutureTaskä»»å¡æ§è¡çæ¶é´æ¯è¾ä¹ ï¼ä¸ä¼å½±åå°å ¶ä»ä»»å¡çè°åº¦æ§è¡ã
ç³»ç»æ¶é´ææ度Timerè°åº¦æ¯åºäºæä½ç³»ç»çç»å¯¹æ¶é´çï¼å¯¹æä½ç³»ç»çæ¶é´ææï¼ä¸æ¦æä½ç³»ç»çæ¶é´æ¹åï¼åTimerçè°åº¦ä¸å精确ã
ScheduledThreadPoolExecutorè°åº¦æ¯åºäºç¸å¯¹æ¶é´çï¼ä¸åæä½ç³»ç»æ¶é´æ¹åçå½±åã
æ¯å¦æè·å¼å¸¸Timerä¸ä¼æè·TimerTaskæåºçå¼å¸¸ï¼å ä¸Timeråæ¯å线ç¨çãä¸æ¦æ个è°åº¦ä»»å¡åºç°å¼å¸¸ï¼åæ´ä¸ªçº¿ç¨å°±ä¼ç»æ¢ï¼å ¶ä»éè¦è°åº¦çä»»å¡ä¹ä¸åæ§è¡ã
ScheduledThreadPoolExecutoråºäºçº¿ç¨æ± æ¥å®ç°è°åº¦åè½ï¼æ个任å¡æåºå¼å¸¸åï¼å ¶ä»ä»»å¡ä»è½æ£å¸¸æ§è¡ã
ä»»å¡æ¯å¦å ·å¤ä¼å 级Timerä¸æ§è¡çTimerTaskä»»å¡æ´ä½ä¸æ²¡æä¼å 级çæ¦å¿µï¼åªæ¯æç §ç³»ç»çç»å¯¹æ¶é´æ¥æ§è¡ä»»å¡ã
ScheduledThreadPoolExecutorä¸æ§è¡çScheduledFutureTaskç±»å®ç°äºjava.lang.Comparableæ¥å£åjava.util.concurrent.Delayedæ¥å£ï¼è¿ä¹å°±è¯´æäºScheduledFutureTaskç±»ä¸å®ç°äºä¸¤ä¸ªé常éè¦çæ¹æ³ï¼ä¸ä¸ªæ¯java.lang.Comparableæ¥å£çcompareToæ¹æ³ï¼ä¸ä¸ªæ¯java.util.concurrent.Delayedæ¥å£çgetDelayæ¹æ³ãå¨ScheduledFutureTaskç±»ä¸compareToæ¹æ³å®ç°äºä»»å¡çæ¯è¾ï¼è·ç¦»ä¸æ¬¡æ§è¡çæ¶é´é´éççä»»å¡ä¼æå¨åé¢ï¼ä¹å°±æ¯è¯´ï¼è·ç¦»ä¸æ¬¡æ§è¡çæ¶é´é´éççä»»å¡çä¼å 级æ¯è¾é«ãègetDelayæ¹æ³åè½å¤è¿åè·ç¦»ä¸æ¬¡ä»»å¡æ§è¡çæ¶é´é´éã
æ¯å¦æ¯æ对任å¡æåºTimerä¸æ¯æ对任å¡çæåºã
ScheduledThreadPoolExecutorç±»ä¸å®ä¹äºä¸ä¸ªéæå é¨ç±»DelayedWorkQueueï¼DelayedWorkQueueç±»æ¬è´¨ä¸æ¯ä¸ä¸ªæåºéåï¼ä¸ºéè¦è°åº¦çæ¯ä¸ªä»»å¡æç §è·ç¦»ä¸æ¬¡æ§è¡æ¶é´é´éç大å°æ¥æåº
è½å¦è·åè¿åçç»æTimerä¸æ§è¡çTimerTaskç±»åªæ¯å®ç°äºjava.lang.Runnableæ¥å£ï¼æ æ³ä»TimerTaskä¸è·åè¿åçç»æã
ScheduledThreadPoolExecutorä¸æ§è¡çScheduledFutureTask类继æ¿äºFutureTaskç±»ï¼è½å¤éè¿Futureæ¥è·åè¿åçç»æã
éè¿ä»¥ä¸å¯¹ScheduledThreadPoolExecutorç±»åTimerç±»çåæ对æ¯ï¼ç¸ä¿¡å¨JDK1.5ä¹åï¼å°±æ²¡æ使ç¨Timeræ¥å®ç°å®æ¶ä»»å¡è°åº¦çå¿ è¦äºã
äºè ç®åç示ä¾è¿éï¼ç»åºä½¿ç¨TimeråScheduledThreadPoolExecutorå®ç°å®æ¶è°åº¦çç®å示ä¾ï¼ä¸ºäºç®ä¾¿ï¼æè¿éå°±ç´æ¥ä½¿ç¨å¿åå é¨ç±»çå½¢å¼æ¥æ交任å¡ã
Timerç±»ç®å示ä¾æºä»£ç 示ä¾å¦ä¸æ示ã
packageio.binghe.concurrent.lab;importjava.util.Timer;importjava.util.TimerTask;/***@authorbinghe*@version1.0.0*@descriptionæµè¯Timer*/publicclassTimerTest{ publicstaticvoidmain(String[]args)throwsInterruptedException{ Timertimer=newTimer();timer.scheduleAtFixedRate(newTimerTask(){ @Overridepublicvoidrun(){ System.out.println("æµè¯Timerç±»");}},,);Thread.sleep();timer.cancel();}}è¿è¡ç»æå¦ä¸æ示ã
æµè¯Timerç±»æµè¯Timerç±»æµè¯Timerç±»æµè¯Timerç±»æµè¯Timerç±»æµè¯Timerç±»æµè¯Timerç±»æµè¯Timerç±»æµè¯Timerç±»æµè¯Timerç±»ScheduledThreadPoolExecutorç±»ç®å示ä¾æºä»£ç 示ä¾å¦ä¸æ示ã
packageio.binghe.concurrent.lab;importjava.util.concurrent.*;/***@authorbinghe*@version1.0.0*@descriptionæµè¯ScheduledThreadPoolExecutor*/publicclassScheduledThreadPoolExecutorTest{ publicstaticvoidmain(String[]args)throwsInterruptedException{ ScheduledExecutorServicescheduledExecutorService=Executors.newScheduledThreadPool(3);scheduledExecutorService.scheduleAtFixedRate(newRunnable(){ @Overridepublicvoidrun(){ System.out.println("æµè¯æµè¯ScheduledThreadPoolExecutor");}},1,1,TimeUnit.SECONDS);//主线ç¨ä¼ç ç§Thread.sleep();System.out.println("æ£å¨å ³é线ç¨æ± ...");//å ³é线ç¨æ± scheduledExecutorService.shutdown();booleanisClosed;//çå¾ çº¿ç¨æ± ç»æ¢do{ isClosed=scheduledExecutorService.awaitTermination(1,TimeUnit.DAYS);System.out.println("æ£å¨çå¾ çº¿ç¨æ± ä¸çä»»å¡æ§è¡å®æ");}while(!isClosed);System.out.println("ææ线ç¨æ§è¡ç»æï¼çº¿ç¨æ± å ³é");}}è¿è¡ç»æå¦ä¸æ示ã
æµè¯æµè¯ScheduledThreadPoolExecutoræµè¯æµè¯ScheduledThreadPoolExecutoræµè¯æµè¯ScheduledThreadPoolExecutoræµè¯æµè¯ScheduledThreadPoolExecutoræµè¯æµè¯ScheduledThreadPoolExecutoræµè¯æµè¯ScheduledThreadPoolExecutoræµè¯æµè¯ScheduledThreadPoolExecutoræµè¯æµè¯ScheduledThreadPoolExecutoræµè¯æµè¯ScheduledThreadPoolExecutoræ£å¨å ³é线ç¨æ± ...æµè¯æµè¯ScheduledThreadPoolExecutoræ£å¨çå¾ çº¿ç¨æ± ä¸çä»»å¡æ§è¡å®æææ线ç¨æ§è¡ç»æï¼çº¿ç¨æ± å ³é注æï¼å ³äºTimeråScheduledThreadPoolExecutorè¿æå ¶ä»ç使ç¨æ¹æ³ï¼è¿éï¼æå°±ç®åååºä»¥ä¸ä¸¤ä¸ªä½¿ç¨ç¤ºä¾ï¼æ´å¤ç使ç¨æ¹æ³å¤§å®¶å¯ä»¥èªè¡å®ç°ã
æ¬æå享èªå为äºç¤¾åºããé«å¹¶åãScheduledThreadPoolExecutorä¸Timerçåºå«åç®å示ä¾ãï¼ä½è ï¼å°æ²³ã
线程池调优之动态参数配置
前言线程池的核心参数配置在网上有一大堆的文章介绍,这次结合个人理解写一篇文章记录一下,源码以便加深印象和后续查阅。源码线程池配置参数
corePoolSize:线程池核心线程数
maximumPoolSize:线程池最大线程数
keepAliveTime:允许线程空闲时间(对非核心工作线程的源码回收)
TimeUnit:线程空闲时间单位
workQueue:线程队列(当核心线程数满了,新的源码任务就会放入这个队列中)
threadFactory:线程工厂(用于创建工作线程,自定义线程工厂可以指定线程名称)
handler:线程池拒绝策略(当线程队列满了且最大线程数也满了,源码psd转源码就会执行任务拒绝策略,源码默认有4种)
allowCoreThreadTimeOut:控制核心工作线程是源码否需要被回收
常规线程池参数配置-首先提问一个面试题:现有个任务,台服务器,源码每台机器都是源码4核,在任务不丢弃情况下,源码线程池参数该怎么配置最合理呢?
-把这个问题拆分一下,源码个任务,源码台机器,源码那么每台机器就负责个任务(常规轮训负载均衡模式,源码不考虑其他额外情况),每台机器都是读源码技能4核,那么就可以设置核心线程数和最大线程数为4,线程队列大小为即可。
-当然也可以把核心和最大线程数设置为5(n+1)个,线程队列大小为,这样是为了防止线程偶尔由于页缺失故障或者其他原因暂停,出多来的一个线程也能确保CPU的调度时钟周期不会被浪费,相当于备用线程。
如果任务是招聘源码网CPU密集型配置:工作线程=cpu核心数+1;
如果任务是IO密集型场景:工作线程=cpu核心数*2;
所以上面例子中就是基于CPU密集型任务配置线程池。而且网上大部分文章描述线程池配置也是基于这两点来分析的。
可惜理想很丰满,现实很骨感。在实际工作场景中,其实没那么容易区分线程中执行的任务是CPU密集还是IO密集,而且服务器上还会有其他应用线程抢占CPU资源,就算还有一些其他的公式计算配置线程池参数,那也是demo源码教程基于理想场景情况下进行配置的,所以上述配置更多的还是应用于面试中。
动态配置线程池参数上述中既然不能一次定义适配所有场景的线程池参数,那么如果可以根据不同业务场景动态配置线程池参数,通过人工干预介入来适配大部分场景也行的
正好在JDK的自定义线程池ThreadPoolExecutor里,提供了动态扩展线程池核心参数的方法
可以在运行期间的线程池使用此方法可以覆盖原来配置的值:
ThreadPoolExecutor线程池提供了5种配置参数可供动态更新:核心线程池,最大线程数,线程工厂,线程空闲时间,源码资本布局拒绝策略。这里主要讨论的是核心线程池和最大线程池两种参数配置:
/****@Author:ZRH*@Date://:*/@Slf4jpublicclassExecutorTest{ publicstaticvoidmain(String[]args)throwsException{ finalThreadPoolExecutorthreadPoolExecutor=newThreadPoolExecutor(2,3,,TimeUnit.SECONDS,newLinkedBlockingQueue<>(7),newThreadPoolExecutor.DiscardPolicy());for(inti=0;i<;i++){ threadPoolExecutor.execute(()->{ try{ logExecutorInfo(threadPoolExecutor);Thread.sleep();}catch(InterruptedExceptione){ }});}logExecutorInfo(threadPoolExecutor);threadPoolExecutor.setCorePoolSize(5);threadPoolExecutor.setMaximumPoolSize(5);logExecutorInfo(threadPoolExecutor);Thread.currentThread().join();}privatestaticvoidlogExecutorInfo(ThreadPoolExecutorexecutor){ log.info("线程池核心线程数="+executor.getCorePoolSize()+",线程池最大线程数="+executor.getMaximumPoolSize()+",线程池队列剩余任务="+executor.getQueue().size()+",线程池活跃线程数="+executor.getActiveCount()+",线程池任务完成数"+executor.getCompletedTaskCount());}}看执行结果:刚开始线程池里核心线程数2个、最大线程数3个、剩下7放队列。活跃的线程也只有3个。
然后更改核心线程和最大线程数为5后,线程池里对应的核心线程数和最大线程数也增加至5个,活跃的工作线程也是5个。说明更改配置成功。
注:更新线程池参数时,核心线程数不能超过最大线程数配置。否则配置最后不会生效。
publicstaticvoidmain(String[]args)throwsException{ finalThreadPoolExecutorthreadPoolExecutor=newThreadPoolExecutor(2,3,,TimeUnit.SECONDS,newLinkedBlockingQueue<>(7),newThreadPoolExecutor.DiscardPolicy());for(inti=0;i<;i++){ threadPoolExecutor.execute(()->{ try{ logExecutorInfo(threadPoolExecutor);Thread.sleep();}catch(InterruptedExceptione){ }});}logExecutorInfo(threadPoolExecutor);threadPoolExecutor.setCorePoolSize(5);//threadPoolExecutor.setMaximumPoolSize(5);logExecutorInfo(threadPoolExecutor);Thread.currentThread().join();}上图中把核心线程数更新为5,最大线程数不改动任为3。最后看执行结果,最终的活跃线程还是3个,说明配置没有生效,具体源码在ThreadPoolExecutor类的getTask()方法里,感兴趣的同学可以去看一下...
动态更新线程队列ThreadPoolExecutor线程池并没有动态配置线程池队列大小的方法
想自己操作一下也是很简单的,只需要自定义实现一个队列,可以直接把LinkedBlockingQueue复制一份,并把capacity参数设定为可更改
publicstaticvoidmain(String[]args)throwsException{ finalThreadPoolExecutorthreadPoolExecutor=newThreadPoolExecutor(2,3,,TimeUnit.SECONDS,newCustomLinkedBlockingQueue<>(7),newThreadPoolExecutor.DiscardPolicy());for(inti=0;i<;i++){ threadPoolExecutor.execute(()->{ try{ logExecutorInfo(threadPoolExecutor);Thread.sleep();}catch(InterruptedExceptione){ }});}logExecutorInfo(threadPoolExecutor);threadPoolExecutor.setCorePoolSize(5);threadPoolExecutor.setMaximumPoolSize(5);CustomLinkedBlockingQueuequeue=(CustomLinkedBlockingQueue)threadPoolExecutor.getQueue();queue.setCapacity();for(inti=0;i<;i++){ threadPoolExecutor.execute(()->{ try{ logExecutorInfo(threadPoolExecutor);Thread.sleep();}catch(InterruptedExceptione){ }});}Thread.currentThread().join();}看结果,后续添加的任务会放入队列中,并且队列大小也超过第一次设置大小,说明配置成功
最后参考:Java线程池实现原理及其在美团业务中的实践
虚心学习,共同进步-_-