原本想给 多线程任务 做一个挂起(暂停)功能,然后配合 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,测试通过。
不过话说回来,感觉“组”概念被削弱,成了鸡肋。