ThreadFactory & ThreadGroup

本秂侑毒 提交于 2020-03-01 23:56:21

原本想给 多线程任务 做一个挂起(暂停)功能,然后配合 httpcomponents-asyncclient 并发测试,结果意外令人汗颜,竟然CPU占用100%。。。

使用VisualVM观察CPU抽样,发现org.apache.http.impl.nio.reactor.AbstractIOReactor.execute()方法总是占用大部分CPU,然而没调用挂起操作时却一切正常。

这挂起操作的其中一环需要中断线程,这些线程均出产自自定义ThreadFactory

public class GroupThreadFactory implements ThreadFactory {
	
    private final ThreadGroup group;
    private final AtomicInteger threadNumber;

    public GroupThreadFactory() {
        this.group = new ThreadGroup("WorkerGroup");
        this.threadNumber = new AtomicInteger(1);
    }

    public Thread newThread(Runnable r) {
        Thread t = new Thread(null, r, "pool-thread-" + threadNumber.getAndIncrement(), 0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }

    public ThreadGroup getGroup() {
        return this.group;
    }
	
}

ThreadGroup能做到中断从属于此线程组的所有线程。

难道asyncclient的内部线程跑到这ThreadGroup里了,然后被中断后导致无限循环?

故意用Eclipse搜索“ThreadFactory”引用关系,果真发现asyncclient实现的ThreadFactory

// org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor
static class DefaultThreadFactory implements ThreadFactory {

    private static volatile int COUNT = 0;

    public Thread newThread(final Runnable r) {
        return new Thread(r, "I/O dispatcher " + (++COUNT));
    }

}

这就费解了……有扯到啥ThreadGroup吗?

回归Thread源码,发现初始化方法:

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
    Thread parent = currentThread();
    SecurityManager security = System.getSecurityManager();
    if (g == null) {
        /* Determine if it's an applet or not */
	    
        /* If there is a security manager, ask the security manager
            what to do. */
        if (security != null) {
        g = security.getThreadGroup();
        }

        /* If the security doesn't have a strong opinion of the matter
            use the parent thread group. */
        if (g == null) {
        g = parent.getThreadGroup();
        }
    }

其中“ g = parent.getThreadGroup(); ”表明最低情况下会采用当前线程的线程组,实际上几乎采用这种方式。

这就解释了为什么内置的ThreadGroup会跑到asyncclient里去了!

尝试过分析修改Thread与ThreadGroup的引用关系,结果无奈放弃,毕竟是Java重要API,哪会随便让人修改。

最后,我从run方法下手。

/**
 * Give up {@link ThreadGroup}
 * @author Adan
 */
public abstract class InterruptableThreadFactory implements ThreadFactory {

    private final String name;
    private final AtomicInteger threadNumber = new AtomicInteger(1);

    private InterruptableThreadFactory(String factoryName){
        this.name = factoryName + "-thread-";
    }
	
    private abstract class IThread extends Thread{
		
        public IThread(ThreadGroup group, Runnable target, String name, long stackSize) {
            super(group, target, name, stackSize);
        }

        protected abstract void started();
		
        @Override public final void run() {
            if(Thread.currentThread() == this){ // the RUNNING thread only
                this.started(); // code here would be safe absolutely
                try {
                    super.run();
                } finally {
                    this.terminated(); 
                }
            }else{
                super.run();
            }
        }
		
        /**
         * invoked by the RUNNING thread
         */
        protected abstract void terminated();
		
    }

    public final Thread newThread(Runnable r) {
        Thread t = new IThread(null, r, this.name+threadNumber.getAndIncrement(), 0) {
            @Override protected void started() {
                hold(this);
            }
            @Override protected void terminated() {
                releaseThread();
            }
        };
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
	
    /**
     * 释放当前线程在此占用的资源。
     */
    final void releaseThread() {
        this.release(Thread.currentThread());
    }
    abstract void release(Thread t) ; // 注意挂起线程时与释放资源时的锁等待冲突
    abstract void hold(Thread t) ;

    public abstract Object[] snapshotThreads() ; // copy on read
    abstract int threadSize() ;
	
    /**
     * 中断所有持有线程。<br/>
     * @see #doInterrupt(Object[])
     * @see #doInterrupt(Iterator)
     */
    public void interruptThreads() {
        this.doInterrupt( this.snapshotThreads() );
    }
    final void doInterrupt(Object[] threads) {
        for (int i = 0; i < threads.length; i++) {
            this.interrupt( (Thread) threads[i] );
        }
    }
    /**
     * @param threadIterator 弱一致的
     */
    final void doInterrupt(Iterator<Thread> threadIterator) { // interruptDirectThreads
        while ( threadIterator.hasNext() ) {
            this.interrupt( threadIterator.next() );
        }
    }
    private final void interrupt(Thread t) {
        if (t.getState() != Thread.State.TERMINATED) t.interrupt();
    }
	
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(50);
        sb.append("InterruptableThreadFactory@").append(Integer.toHexString(super.hashCode()))
        .append("(threadSize=").append(this.threadSize()).append(')');
        return sb.toString();
    }

	
	
    public static InterruptableThreadFactory newInstance(String factoryName, boolean large){
        if(large){
            return new InterruptableThreadFactory(factoryName){
                ...
            };
        }else{
            return new InterruptableThreadFactory(factoryName){
				
                @SuppressWarnings("serial")
                private final Collection<Thread> threadSet = new java.util.ArrayList<Thread>(){         @Override public synchronized final boolean remove(Object o){return super.remove(o);}
        @Override public synchronized final boolean add(Thread e){return super.add(e);}
        @Override public synchronized final Object[] toArray(){return super.toArray();}
        @Override public synchronized final int size() { return super.size(); }
                };
				
                @Override final void release(Thread t) {
                    this.threadSet.remove(t);
                }
                @Override final void hold(Thread t) {
                    this.threadSet.add(t);
                }
                @Override public final Object[] snapshotThreads() {
                    Object[] threads = this.threadSet.toArray();
                    return threads;
                }
                @Override final int threadSize() {
                    return this.threadSet.size();
                }
				
            };
        }
    }
	
}


OK,测试通过。

不过话说回来,感觉“组”概念被削弱,成了鸡肋。













易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!