博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浅谈tomcat的ThreadLocalLeakPreventionListener实现原理
阅读量:4213 次
发布时间:2019-05-26

本文共 4666 字,大约阅读时间需要 15 分钟。

为什么需要threadlocalLeakPerventionListener

    当context reload的时候,如果正在执行的worker线程引用了threadlocal中的变量,会造成整个webclassloader回收不了造成内存泄露,具体请移步tomcat的wiki ,wiki上比我说得明白。那具体方案也很明确,当context reload的时候,renew线程中的所有的线程,呵呵这活儿得交给listener来做了,所以就有了ThreadLocalLeakPreventionListener,那如何使线程停下来呢,stop这是一个不靠谱的方法,在api已将该方法废弃,幸亏我们还有异常,线程池中的线程遇到异常就会自杀。说多都是废话,还是代码实际

 

private void stopIdleThreads(Context context) {        if (serverStopping) return;        if (context instanceof StandardContext &&            !((StandardContext) context).getRenewThreadsWhenStoppingContext()) {            log.debug("Not renewing threads when the context is stopping, "                + "it is configured not to do it.");            return;        }        Engine engine = (Engine) context.getParent().getParent();        Service service = engine.getService();        Connector[] connectors = service.findConnectors();        if (connectors != null) {            for (Connector connector : connectors) {               //获取该context中的所有connector,并获取thread pool                ProtocolHandler handler = connector.getProtocolHandler();                Executor executor = null;                if (handler != null) {                    executor = handler.getExecutor();                }                if (executor instanceof ThreadPoolExecutor) {                    ThreadPoolExecutor threadPoolExecutor =                        (ThreadPoolExecutor) executor;                    threadPoolExecutor.contextStopping();                } else if (executor instanceof StandardThreadExecutor) {                    StandardThreadExecutor stdThreadExecutor =                        (StandardThreadExecutor) executor;                    stdThreadExecutor.contextStopping();                }            }        }    }

 

public void contextStopping() {       //这个时间很关键,当worker线程执行完毕,会根据这个时间判断是否自杀        this.lastContextStoppedTime.set(System.currentTimeMillis());        // save the current pool parameters to restore them later        int savedCorePoolSize = this.getCorePoolSize();        TaskQueue taskQueue =                getQueue() instanceof TaskQueue ? (TaskQueue) getQueue() : null;        if (taskQueue != null) {            // note by slaurent : quite oddly threadPoolExecutor.setCorePoolSize            // checks that queue.remainingCapacity()==0. I did not understand            // why, but to get the intended effect of waking up idle threads, I            // temporarily fake this condition.            taskQueue.setForcedRemainingCapacity(Integer.valueOf(0));        }        // setCorePoolSize(0) wakes idle threads        this.setCorePoolSize(0);        // wait a little so that idle threads wake and poll the queue again,        // this time always with a timeout (queue.poll() instead of        // queue.take())        // even if we did not wait enough, TaskQueue.take() takes care of timing        // out, so that we are sure that all threads of the pool are renewed in        // a limited time, something like         // (threadKeepAlive + longest request time)        try {            Thread.sleep(200L);        } catch (InterruptedException e) {            // yes, ignore        }        if (taskQueue != null) {            // ok, restore the state of the queue and pool            taskQueue.setForcedRemainingCapacity(null);        }        this.setCorePoolSize(savedCorePoolSize);    }

 

protected void stopCurrentThreadIfNeeded() {        if (currentThreadShouldBeStopped()) {            long lastTime = lastTimeThreadKilledItself.longValue();           判断这个线程是否需要自杀,就是根据context stop的时间+delay是否小于当前,如小于抛出异常自杀            if (lastTime + threadRenewalDelay < System.currentTimeMillis()) {                if (lastTimeThreadKilledItself.compareAndSet(lastTime,                        System.currentTimeMillis() + 1)) {                    // OK, it's really time to dispose of this thread                    final String msg = sm.getString(                                    "threadPoolExecutor.threadStoppedToAvoidPotentialLeak",                                    Thread.currentThread().getName());                    Thread.currentThread().setUncaughtExceptionHandler(                            new UncaughtExceptionHandler() {                                @Override                                public void uncaughtException(Thread t,                                        Throwable e) {                                    // yes, swallow the exception                                    log.debug(msg);                                }                            });                    throw new RuntimeException(msg);                }            }        }    }

 

  粗略地marker一下thread renew的整个过程,如有想法,敬请拍砖 

转载地址:http://lmdmi.baihongyu.com/

你可能感兴趣的文章
Redis Cluster的FailOver失败案例分析
查看>>
Android Alarm驱动源代码分析(Alarm.c)
查看>>
S3C2440上LCD驱动 (FrameBuffer)实例开发讲解
查看>>
Linux音频编程指南
查看>>
usb-otg-调试心得
查看>>
USB规范浏览--设备和主机规范
查看>>
男人的品位--我们自己的最求
查看>>
Android (Linux) Suspend流程
查看>>
LINUX时间管理
查看>>
定时器的使用
查看>>
为Android加入busybox工具
查看>>
使用技巧busybox
查看>>
如何查看与/dev/input目录下的event对应的设备
查看>>
bootloader-bootable解析
查看>>
bootloader (LK)&&android lk bootloader中相关修改指南
查看>>
SD卡驱动分析--基于高通平台
查看>>
SD Card 驱动流程分析
查看>>
Linux之debugfs介绍
查看>>
关于sd卡中一些概念的理解
查看>>
sd卡驱动分析之相关硬件操作和总结
查看>>