精华内容
下载资源
问答
  • java线程池监控

    2018-12-02 16:19:00
    在开发环境和测试环境验证没有任何问题,但是在程序在生产运行一段时间后,发现没有得到自己想要的结果,为此开始了漫长的排查bug的之路,因为用到了一些线程,但是实际又没有对这些线程足够的监控,所以在排查问题...

    原因

    最近在完善公司的基础发布平台的时候,使用到了一线程去做一些异步的事情,在开发环境和测试环境验证没有任何问题,但是在程序在生产运行一段时间后,发现没有得到自己想要的结果,为此开始了漫长的排查bug的之路,因为用到了一些线程,但是实际又没有对这些线程足够的监控,所以在排查问题的时候也是历经艰难险阻;

     

    原始代码

    
    

    protected ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);


    /**
    * 同步应用的jenkins状态
    */
    public void threadASyncAppJenkinsStatus() {
    executorService.scheduleAtFixedRate(() -> {
    List<ArchitectureApp> architectureApps = architectureAppMapper.listArchitectureApp();
    architectureApps.parallelStream().forEach(architectureApp -> syncJenkinsBuild(architectureApp.getName(), ArchitectureType.APP));

    }, 0, 6, TimeUnit.SECONDS);

    }

    /**
    * 同步组件的jenkins状态
    */
    public void syncComponentJenkinsStatus() {
    executorService.scheduleAtFixedRate(() -> {
    List<ArchitectureComponent> architectureComponents = architectureComponentMapper.listArchitectureComponent();
    architectureComponents.parallelStream().forEach(architectureComponent -> syncJenkinsBuild(architectureComponent.getName(), ArchitectureType.COMPONENT));
    }, 0, 6, TimeUnit.SECONDS);

    }
     

    这是其中一部分的代码,做的事情很简单,程序每隔6s就去轮询组件和应用的状态,然后后面我会通过websocket同步到前端页面。这是一段很简单的代码,很难想象这段代码可能出错。但是事与愿违,通过开发和测试环境的测试,在上到生产运行了两天发现前端页面的jenkins状态并没有同步。而通过查看日志,也没法观察问题出在哪,所以只能另寻他法;

     

    ExecutorsMonitor线程监控类

    以下是我们开发的一个线程池工具类,该工具类扩展ScheduledThreadPoolExecutor实现了线程池监控功能,能实时将线程池使用信息打印到日志中,方便我们进行问题排查、系统调优。具体代码如下

    @Slf4j
    class ExecutorsMonitor extends ScheduledThreadPoolExecutor {
    
    
        private ConcurrentHashMap<String, Date> startTimes;
        private String poolName;
    
        /**
         * 调用父类的构造方法,并初始化HashMap和线程池名称
         *
         * @param corePoolSize 线程池核心线程数
         * @param poolName     线程池名称
         */
        public ExecutorsMonitor(int corePoolSize, String poolName) {
            super(corePoolSize);
            this.startTimes = new ConcurrentHashMap<>();
            this.poolName = poolName;
        }
    
        /**
         * 线程池延迟关闭时(等待线程池里的任务都执行完毕),统计线程池情况
         */
        @Override
        public void shutdown() {
            super.shutdown();
        }
    
        /**
         * 线程池立即关闭时,统计线程池情况
         */
        @Override
        public List<Runnable> shutdownNow() {
            return super.shutdownNow();
        }
    
        /**
         * 任务执行之前,记录任务开始时间
         */
        @Override
        protected void beforeExecute(Thread t, Runnable r) {
            startTimes.put(String.valueOf(r.hashCode()), new Date());
        }
    
        /**
         * 任务执行之后,计算任务结束时间
         */
        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            Date startDate = startTimes.remove(String.valueOf(r.hashCode()));
            Date finishDate = new Date();
            long diff = finishDate.getTime() - startDate.getTime();
            // 统计任务耗时、初始线程数、核心线程数、正在执行的任务数量、已完成任务数量、任务总数、队列里缓存的任务数量、池中存在的最大线程数、最大允许的线程数、线程空闲时间、线程池是否关闭、线程池是否终止
            log.info(String.format(this.poolName
                            + "-pool-monitor: Duration: %d ms, PoolSize: %d, CorePoolSize: %d, Active: %d, Completed: %d, Task: %d, Queue: %d, LargestPoolSize: %d, MaximumPoolSize: %d,  KeepAliveTime: %d, isShutdown: %s, isTerminated: %s",
                    diff, this.getPoolSize(), this.getCorePoolSize(), this.getActiveCount(), this.getCompletedTaskCount(), this.getTaskCount(),
                    this.getQueue().size(), this.getLargestPoolSize(), this.getMaximumPoolSize(), this.getKeepAliveTime(TimeUnit.MILLISECONDS),
                    this.isShutdown(), this.isTerminated()));
        }
    
        public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, String poolName) {
            return new ExecutorsMonitor(corePoolSize, poolName);
        }
    
    }
    

      

    后来在生产终于定位问题,发现线程内部后来停止,同时发现的还有报错,通过查阅资料发现,原来线程发生异常后会退出,通过try catch很好的解决了这个问题

     

    转载于:https://www.cnblogs.com/clovejava/p/10053916.html

    展开全文
  • Quote: 引用1楼longtian1213的回复:用submit方法,返回一个Future,然后调用Future中的Vget(longtimeout,TimeUnitunit)方法,timeout就是超时时间,超时没返回结果就会报异常,捕获异常,然后调用cancel方法你是说...

    Quote: 引用 1 楼 longtian1213 的回复:

    用submit方法,返回一个Future,然后调用Future中的V get(long timeout, TimeUnit unit)方法,timeout就是超时时间,超时没返回结果就会报异常,捕获异常,然后调用cancel方法

    你是说这样么?

    for(...){

    futures.add( pool.submit(r););

    }

    for( Future> future : futures){

    try {

    future.get(5, TimeUnit.SECONDS);

    } catch (InterruptedException e) {

    System.out.println("线程中断出错。");

    future.cancel(true);// 中断执行此任务的线程

    } catch (ExecutionException e) {

    System.out.println("线程服务出错。");

    future.cancel(true);// 中断执行此任务的线程

    } catch (TimeoutException e) {// 超时异常

    System.out.println("超时。");

    future.cancel(true);// 中断执行此任务的线程

    }

    }

    在我这里这么做,好像线程并没有同步运行。

    展开全文
  • 我们会使用各种池化技术缓存 创建性能开销较大的 对象,比如线程池、连接池、内存池。它们的原理都是预先创建一些对象入池,使用时直接取出,用完归还以复用,还会通过策略调整池中缓存对象的数量,实现动态伸缩性。...

    我们会使用各种池化技术缓存 创建性能开销较大的 对象,比如线程池、连接池、内存池。

    它们的原理都是预先创建一些对象入池,使用时直接取出,用完归还以复用,还会通过策略调整池中缓存对象的数量,实现动态伸缩性。

    由于线程的创建比较昂贵,短平快的任务一般考虑使用线程池处理,而非直接创建线程。

    手动声明线程池

    JDK的Executors工具类定义了很多便捷的方法可以快速创建线程池。

    7e70336ee777aa9e7f2b3504333f1552.png

    但是阿里有话说:

    1bb2add375f86a45b5b77f649465db99.png我们来看他说的弊端案例真的这么严重吗?

    newFixedThreadPool 可能 OOM

    我们写一段测试代码,来初始化一个单线程的FixedThreadPool,循环1亿次向线程池提交任务,每个任务都会创建一个比较大的字符串然后休眠一小时:

    a902dfe26797cfb7be94bd1317d42049.png

    执行程序后不久,日志中就出现了如下OOM:Exception in thread "http-nio-45678-ClientPoller"

    java.lang.OutOfMemoryError: GC overhead limit exceeded复制代码

    c2525e31cd89b4267af5c3ecc0a1503e.pngnewFixedThreadPool线程池的工作队列直接new了一个LinkedBlockingQueue

    8b61b39a50ec8f1e126104178e6f66dc.png但其默认构造器是一个Integer.MAX_VALUE长度的队列,所以很快就队列满了

    9f2b5c0c2f33789b28d525df63fc2cfa.png

    虽然使用newFixedThreadPool可以固定工作线程数量,但任务队列几乎***。如果任务较多且执行较慢,队列就会快速积压,内存不够就很容易导致OOM。

    newCachedThreadPool导致OOM[11:30:30.487] [http-nio-45678-exec-1] [ERROR] [.a.c.c.C.[.[.[/].[dispatcherServlet]:175 ] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: unable to create new native thread] with root cause

    java.lang.OutOfMemoryError: unable to create new native thread

    复制代码

    日志可见OOM是因为无法创建线程,newCachedThreadPool这种线程池的最大线程数是Integer.MAX_VALUE,可认为无上限,而其工作队列SynchronousQueue是一个没有存储空间的阻塞队列。

    所以只要有请求到来,就必须找到一条工作线程处理,若当前无空闲线程就再创建一个新的。

    由于我们的任务需1小时才能执行完成,大量任务进来后会创建大量的线程。我们知道线程是需要分配一定的内存空间作为线程栈的,比如1MB,因此无限创建线程必然会导致OOM:public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                  60L, TimeUnit.SECONDS,                                  new SynchronousQueue());复制代码

    开发同学其实面试时都知道这俩线程池原理,只是抱有侥幸,觉得只是使用线程池做了轻量任务,不会造成队列积压或开启大量线程。

    案例

    用户注册后,我们调用一个外部服务去发送短信,发送短信接口正常时可在100ms内响应,TPS 100的注册量,CachedThreadPool能稳定在占用10个左右线程的情况下满足需求。在某个时间点,外部短信服务不可用了,我们调用这个服务的超时又特别长, 比如1分钟,1分钟可能就进来了6000用户,产生6000个发送短信的任务,需要6000个线程,没多久就因为无法创建线程导致了OOM。

    所以阿里也不建议使用Executors提供的两种方便线程池创建方式:需根据自己的场景、并发情况来评估线程池的几个核心参数,包括核心线程数、最大线程数、线程回收策略、工作队列的类型,以及拒绝策略,确保线程池的工作行为符合需求,一般都需要设置有界的工作队列和可控的线程数

    任何时候都应为自定义线程池指定有意义的名称,以方便排查问题。当出现线程数量暴增、线程死锁、线程占用大量CPU、线程执行出现异常等问题时,往往会抓取线程栈。此时,有意义的线程名称,就可以方便定位问题。

    除手动声明线程池外,推荐用些监控手段观察线程池状态。线程池这个组件往往会表现得任劳任怨、默默无闻,除非出现拒绝策略,否则压力再大都不会抛异常。若能提前观察到线程池队列的积压或线程数量的快速膨胀,往往可提早发现并解决问题。

    线程池线程管理如下方法实现最简陋的监控

    6dd047b38dcefd5269b1436c5b9f4340.png

    自定义个线程池,借助Jodd类库的ThreadFactoryBuilder方法来构造一个线程工厂,实现线程池线程的自定义命名。

    然后,我们写一段测试代码来观察线程池管理线程的策略。测试代码的逻辑为,每次间隔1秒向线程池提交任务,循环20次,每个任务需要10秒才能执行完成,代码如下:发现提交失败的记录,日志就像这样

    0fc7e58aa3477084e6da6cbdeaef3d34.png

    线程池默认行为不会初始化corePoolSize个线程,有任务来了才创建工作线程

    核心线程满后不会立即扩容线程池,而是把任务堆积到工作队列

    工作队列满后扩容线程池,直至线程数达到maximumPoolSize

    若队列已满且达最大线程后,还有任务来按拒绝策略处理

    当线程数大于核心线程数时,线程等待keepAliveTime后还是无任务需要处理,收缩线程到核心线程数

    了解这个策略,有助于我们根据实际的容量规划需求,为线程池设置合适的初始化参数。也可通过一些手段来改变这些默认工作行为,比如:声明线程池后立即调用prestartAllCoreThreads方法,来启动所有核心线程

    传true给allowCoreThreadTimeOut,让线程池在空闲时同样回收核心线程

    Java线程池是先用工作队列来存放来不及处理的任务,满后再扩容线程池。当工作队列设置很大时(那个默认工具类),最大线程数这个参数就没啥意义了,因为队列很难满或到满时可能已OOM,更没机会去扩容线程池了。

    是否能让线程池优先开启更多线程,而把队列当成后续方案?

    比如案例的任务执行得很慢,需要10s,若线程池可优先扩容到5个最大线程,那么这些任务最终都可以完成,而不会因为线程池扩容过晚导致慢任务来不及处理。

    实现思路

    实现基本就如下两个难题:线程池在工作队列满了无法入队的情况下会扩容线程池,那是否可重写队列的offer,人为制造该队列已满的假象?

    Hack了队列,在达到最大线程后势必会触发拒绝策略,那么能否实现一个自定义的拒绝策略处理程序,这个时候再把任务真正插入队列?

    Tomcat其实已经实现了类似的“弹性”线程池。务必确认清楚线程池本身是不是复用的某项目生产环境偶尔报警线程数过多,超过2000个,收到报警后查看监控发现,瞬时线程数比较多但过一会儿又会降下来,线程数抖动很厉害,而应用的访问量变化不大。

    为定位问题,在线程数较高时抓取线程栈,发现内存中有1000多个自定义线程池。一般来说,线程池肯定是复用的,有5个以内的线程池都可认为正常,但1000多个线程池肯定不正常。

    在项目代码也没看到声明线程池,搜索execute关键字后定位到,原来是业务代码调用了一个类库来获得线程池,类似如下:

    调用ThreadPoolHelper的getThreadPool方法来获得线程池,然后提交数个任务到线程池处理,看不出什么异常。

    880140019934a3bf577dd2fcbdecc9a9.png但getThreadPool方法居然是每次都使用Executors.newCachedThreadPool来创建一个线程池。

    d3d1647159643fedbd69f57ae744e7ce.pngnewCachedThreadPool会在需要时创建必要多的线程,业务代码的一次业务操作会向线程池提交多个慢任务,这样执行一次业务操作就会开启多个线程。如果业务操作并发量较大的话,的确有可能一下子开启几千个线程。

    那为什么我们能在监控中看到线程数量会下降,而不OOM?

    newCachedThreadPool的核心线程数是0,而keepAliveTime是60s,即60s后所有的线程都可回收。

    修复

    使用静态字段存放线程池的引用,返回线程池的代码直接返回这个静态字段即可。

    8ea1b754ababd875da53c4984db62a7d.png

    考虑线程池的混用

    线程池的意义在于复用,那这是不是意味着程序应该始终使用一个线程池?

    不是。要根据任务优先级指定线程池的核心参数,包括线程数、回收策略和任务队列。

    案例

    业务代码使用线程池异步处理一些内存中的数据,但监控发现处理得很慢,整个处理过程都是内存中的计算不涉及I/O操作,也需要数s处理时间,应用程序CPU占用也不是很高。

    最终排查发现业务代码使用的线程池,还被一个后台文件批处理任务用了。

    e1603f5a73486f3df64d049a45849d10.png

    模拟一下文件批处理,在程序启动后通过一个线程开启死循环逻辑,不断向线程池提交任务,任务的逻辑是向一个文件中写入大量的数据:复制代码

    可以想象到,这个线程池中的2个线程任务是相当重的。通过printStats方法打印出的日志,我们观察下线程池的负担:

    c4987c5371e4d39b8a8326772a084bc0.png

    线程池的2个线程始终处活跃状态,队列也基本满。因为开启了CallerRunsPolicy拒绝处理策略,所以当线程满队列满,任务会在提交任务的线程或调用execute方法的线程执行,也就是说不能认为提交到线程池的任务就一定是异步处理的。

    若使用CallerRunsPolicy,有可能异步任务变同步执行。从日志的第四行也可以看到这点。这也是这个拒绝策略比较特别的原因。

    不知道写代码的同学为什么设置这个策略,或许是测试时发现线程池因为任务处理不过来出现了异常,而又不希望线程池丢弃任务,所以最终选择了这样的拒绝策略。不管怎样,这些日志足以说明线程池饱和了。

    业务代码复用这样的线程池来做内存计算就难搞了。向线程池提交一个简单任务

    0047c6d5955c7ae51aeac0bad1cb9871.png简单压测TPS为85,性能差

    b56c1563a11dfea2e1c74754c9125584.png

    问题没这么简单。原来执行IO任务的线程池使用CallerRunsPolicy,所以直接使用该线程池进行异步计算,当线程池饱和的时候,计算任务会在执行Web请求的Tomcat线程执行,这时就会进一步影响到其他同步处理的线程,甚至造成整个应用程序崩溃。

    修正

    使用独立的线程池来做这样的“计算任务”。

    模拟代码执行的是休眠操作,并不属于CPU绑定的操作,更类似I/O绑定的操作,若线程池线程数设置太小会限制吞吐能力:

    d4add5043111b09337a7c7ef8fa39f29.png使用单独的线程池改造代码后再来测试一下性能,TPS提高到1683

    2989c8bf18f4525307b3ec178b0430fb.png

    可见盲目复用线程池混用线程的问题在于,别人定义的线程池属性不一定适合你的任务,混用会相互干扰。

    就好比,我们往往会用虚拟化技术来实现资源的隔离,而不是让所有应用程序都直接使用物理机。

    线程池混用:Java 8的parallel stream

    可方便并行处理集合中的元素,共享同一ForkJoinPool,默认并行度是CPU核数-1。对于CPU绑定的任务,使用这样的配置较合适,但若集合操作涉及同步IO操作的话(比如数据库操作、外部服务调用等),建议自定义一个ForkJoinPool(或普通线程池)。

    参考《阿里巴巴Java开发手册》

    展开全文
  • Java线程池监控小结

    2019-03-08 19:26:46
    最近我们组杨青同学遇到一个使用线程池不当的问题:异步处理的线程池线程将主线程hang住了,分析代码发现是线程池的拒绝策略设置得不合理,设置为CallerRunsPolicy。当异步线程的执行效率降低时,阻塞队列满了,触发...

    最近我们组杨青同学遇到一个使用线程池不当的问题:异步处理的线程池线程将主线程hang住了,分析代码发现是线程池的拒绝策略设置得不合理,设置为CallerRunsPolicy。当异步线程的执行效率降低时,阻塞队列满了,触发了拒绝策略,进而导致主线程hang死。

    从这个问题中,我们学到了两点:

    • 线程池的使用,需要充分分析业务场景后作出选择,必要的情况下需要自定义线程池;
    • 线程池的运行状况,也需要监控

    关于线程池的监控,我参考了《Java编程的艺术》中提供的思路实现的,分享下我的代码片段,如下:

    public class AsyncThreadExecutor implements AutoCloseable {
    
        private static final int DEFAULT_QUEUE_SIZE = 1000;
    
        private static final int DEFAULT_POOL_SIZE = 10;
    
        @Setter
        private int queueSize = DEFAULT_QUEUE_SIZE;
    
        @Setter
        private int poolSize = DEFAULT_POOL_SIZE;
    
        /**
         * 用于周期性监控线程池的运行状态
         */
        private final ScheduledExecutorService scheduledExecutorService =
            Executors.newSingleThreadScheduledExecutor(new BasicThreadFactory.Builder().namingPattern("async thread executor monitor").build());
    
        /**
         * 自定义异步线程池
         * (1)任务队列使用有界队列
         * (2)自定义拒绝策略
         */
        private final ThreadPoolExecutor threadPoolExecutor =
            new ThreadPoolExecutor(poolSize, poolSize, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue(queueSize),
                                   new BasicThreadFactory.Builder().namingPattern("async-thread-%d").build(),
                                   (r, executor) -> log.error("the async executor pool is full!!"));
        private final ExecutorService executorService = threadPoolExecutor;
    
        @PostConstruct
        public void init() {
            scheduledExecutorService.scheduleAtFixedRate(() -> {
                /**
                 * 线程池需要执行的任务数
                 */
                long taskCount = threadPoolExecutor.getTaskCount();
                /**
                 * 线程池在运行过程中已完成的任务数
                 */
                long completedTaskCount = threadPoolExecutor.getCompletedTaskCount();
                /**
                 * 曾经创建过的最大线程数
                 */
                long largestPoolSize = threadPoolExecutor.getLargestPoolSize();
                /**
                 * 线程池里的线程数量
                 */
                long poolSize = threadPoolExecutor.getPoolSize();
                /**
                 * 线程池里活跃的线程数量
                 */
                long activeCount = threadPoolExecutor.getActiveCount();
    
                log.info("async-executor monitor. taskCount:{}, completedTaskCount:{}, largestPoolSize:{}, poolSize:{}, activeCount:{}",
                         taskCount, completedTaskCount, largestPoolSize, poolSize, activeCount);
            }, 0, 10, TimeUnit.MINUTES);
        }
    
        public void execute(Runnable task) {
            executorService.execute(task);
        }
    
        @Override
        public void close() throws Exception {
            executorService.shutdown();
        }
    }
    

    这里的主要思路是:(1)使用有界队列的固定数量线程池;(2)拒绝策略是将任务丢弃,但是需要记录错误日志;(3)使用一个调度线程池对业务线程池进行监控。

    在查看监控日志的时候,看到下图所示的监控日志:

     

    屏幕快照 2018-03-28 21.55.19.png

    这里我对largestPooSize的含义比较困惑,按字面理解是“最大的线程池数量”,但是按照线程池的定义,maximumPoolSize和coreSize相同的时候(在这里,都是10),一个线程池里的最大线程数是10,那么为什么largestPooSize可以是39呢?我去翻这块的源码:

        /**
         * Returns the largest number of threads that have ever
         * simultaneously been in the pool.
         *
         * @return the number of threads
         */
        public int getLargestPoolSize() {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                return largestPoolSize;
            } finally {
                mainLock.unlock();
            }
        }
    

    注释的翻译是:返回在这个线程池里曾经同时存在过的线程数。再看这个变量largestPoolSize在ThreadExecutor中的赋值的地方,代码如下:

    private boolean addWorker(Runnable firstTask, boolean core) {
            retry:
            for (;;) {
                int c = ctl.get();
                int rs = runStateOf(c);
    
                // Check if queue empty only if necessary.
                if (rs >= SHUTDOWN &&
                    ! (rs == SHUTDOWN &&
                       firstTask == null &&
                       ! workQueue.isEmpty()))
                    return false;
    
                for (;;) {
                    int wc = workerCountOf(c);
                    if (wc >= CAPACITY ||
                        wc >= (core ? corePoolSize : maximumPoolSize))
                        return false;
                    if (compareAndIncrementWorkerCount(c))
                        break retry;
                    c = ctl.get();  // Re-read ctl
                    if (runStateOf(c) != rs)
                        continue retry;
                    // else CAS failed due to workerCount change; retry inner loop
                }
            }
    
            boolean workerStarted = false;
            boolean workerAdded = false;
            Worker w = null;
            try {
                w = new Worker(firstTask);
                final Thread t = w.thread;
                if (t != null) {
                    final ReentrantLock mainLock = this.mainLock;
                    mainLock.lock();
                    try {
                        // Recheck while holding lock.
                        // Back out on ThreadFactory failure or if
                        // shut down before lock acquired.
                        int rs = runStateOf(ctl.get());
    
                        if (rs < SHUTDOWN ||
                            (rs == SHUTDOWN && firstTask == null)) {
                            if (t.isAlive()) // precheck that t is startable
                                throw new IllegalThreadStateException();
                            workers.add(w);
                            int s = workers.size();
                            if (s > largestPoolSize)
                                largestPoolSize = s;//这里这里!
                            workerAdded = true;
                        }
                    } finally {
                        mainLock.unlock();
                    }
                    if (workerAdded) {
                        t.start();
                        workerStarted = true;
                    }
                }
            } finally {
                if (! workerStarted)
                    addWorkerFailed(w);
            }
            return workerStarted;
        }
    

    发现两点:

    • largestPoolSize是worker集合的历史最大值,只增不减。largestPoolSize的大小是线程池曾创建的线程个数,跟线程池的容量无关;
    • largestPoolSize<=maximumPoolSize。

    PS:杨青同学是这篇文章的灵感来源,他做了很多压测。给了我很多思路,并跟我一起分析了一些代码。

     



    作者:javaadu
    链接:https://www.jianshu.com/p/d05c488b84cc
    来源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

    展开全文
  • Java线程池实现原理及其在美团业务中的实践 线程池是什么 线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。 线程过多会带来额外的开销,其中包括创建销毁线程的开销、...
  • finally { processWorkerExit(w, completedAbruptly); }
  • Java线程池监控

    千次阅读 2015-10-18 19:35:15
    Java线程池监控 如果想实现线程池的监控,必须要自定义线程池继承ThreadPoolExecutor类,并且实现beforeExecute,afterExecute和terminated方法,我们可以在任务执行前,执行后和线程池关闭前干一些事情。如监控...
  • 主要介绍了Java线程池运行状态监控实现解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 扩展ThreadPoolExecutor实现线程池监控 https://my.oschina.net/dabird/blog/1527867 Java线程池监控小结 https://www.jianshu.com/p/d05c488b84cc
  • 2021版Java线程池教程18:如何监控线程池?介绍 getActiveCount、getPoolSize、getLargestPoolSize、getTaskCount、getCompletedTaskCount、getQueue、beforeExecutor、afterExecutor、terminated 方法的作用及用法...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 42,417
精华内容 16,966
关键字:

java线程池监控

java 订阅