本文共 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/