论坛首页 Java企业应用论坛

浅谈tomcat的ThreadLocalLeakPreventionListener实现原理

浏览 5194 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-12-30  

为什么需要threadlocalLeakPerventionListener

    当context reload的时候,如果正在执行的worker线程引用了threadlocal中的变量,会造成整个webclassloader回收不了造成内存泄露,具体请移步tomcat的wiki http://wiki.apache.org/tomcat/MemoryLeakProtection,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的整个过程,如有想法,敬请拍砖 

   发表时间:2011-12-31  
这个方案应该是只能停止空闲线程吧?不知如何类似weblogic的强制停止功能?

强制停止线程需要考虑线程的如下几种情况:

1、线程正在执行一个无限循环:这种情况下由于JVM一直处于运行状态,无法中断,因此不可能停止;
2、线程正在等待一个竞争资源,比如等待其它线程的锁资源:这种情况下可以通过线程中断的方式实现线程停止;
3、线程正在进行数据库操作,比如正在执行一个长时间的查询:这种情况下使用线程中断应该是不能中止线程的,但可以通过强制中断底层数据库连接的方式实现线程中止;
4、线程正在等待文件等IO资源:应该也是可以通过线程中断实现线程中止的;

我能想到的只有线程中断方式了,不知还有没有其它方式可以实现活动线程的安全中止?
0 请登录后投票
   发表时间:2012-01-04  
jxb8901 写道
这个方案应该是只能停止空闲线程吧?不知如何类似weblogic的强制停止功能?

强制停止线程需要考虑线程的如下几种情况:

1、线程正在执行一个无限循环:这种情况下由于JVM一直处于运行状态,无法中断,因此不可能停止;
2、线程正在等待一个竞争资源,比如等待其它线程的锁资源:这种情况下可以通过线程中断的方式实现线程停止;
3、线程正在进行数据库操作,比如正在执行一个长时间的查询:这种情况下使用线程中断应该是不能中止线程的,但可以通过强制中断底层数据库连接的方式实现线程中止;
4、线程正在等待文件等IO资源:应该也是可以通过线程中断实现线程中止的;

我能想到的只有线程中断方式了,不知还有没有其它方式可以实现活动线程的安全中止?


您好,您说的线程安全终止,这里的“安全”是指什么,能说明一下吗?
是不影响目前系统的运行就叫“安全”吗?如果线程正在做业务数据相关的更新或是文件IO,这时中断可能会造成
数据的混乱,这种是不在您说的“安全”范围之内的吗?
0 请登录后投票
   发表时间:2012-01-04  
   根据threadlocalLeak的应用场景,只需使正在运行的线程死亡,空闲的线程不涉及threadlocal的引用。
    仁兄分析得透彻,这种方案是在线程的run方法执行结束之后,采用抛出异常的方式来终止线程,如果是线程是执行的无限循环那就无能为力了

    可否介绍一下weblogic的强制停止功能?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics