我有一个最近开始使用100%CPU时间的Java进程。使用jdb
我发现这是由ThreadPoolExecutor
重复创建线程引起的。
有问题的执行人被定义为:
private final ScheduledExecutorService _scheduler = Executors.newScheduledThreadPool(0, new NamedThreadFactory("OrderServiceScheduler", true, null));
计划的唯一任务是:
_scheduler.scheduleAtFixedRate(new Runnable() { @Override public void run() { s_log.info("Resetting order books"); _liveOrderBook.clear(); } },
midnightTodayInMs,
TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS),
TimeUnit.MILLISECONDS);
永远不会打印日志语句(我99%确定为此记录器启用了INFO
级别日志记录)。
我首先在jdb中运行trace go methods
并看到很多行:
Method entered: "thread=OrderServiceScheduler-thread-22237794", com.kbcfp.util.NamedThreadFactory.newThread(), line=45 bci=0
Method entered: "thread=OrderServiceScheduler-thread-22237794", org.apache.log4j.helpers.ThreadLocalMap.childValue(), line=34 bci=0
Method exited: return value = null, "thread=OrderServiceScheduler-thread-22237794", org.apache.log4j.helpers.ThreadLocalMap.childValue(), line=38 bci=15
Method exited: return value = instance of java.lang.Thread(name='OrderServiceScheduler-thread-22237795', id=6388), "thread=OrderServiceScheduler-thread-22237794", com.kbcfp.util.NamedThreadFactory.newThread(), line=52 bci=68
所以我在org.apache.log4j.helpers.ThreadLocalMap.childValue
中设置了一个断点并继续进行,直到线程意外停止执行(紧跟在最后next, thread
OrderServiceScheduler-thread-151389734 isn't listed in the output of
线程之后):
> stop in org.apache.log4j.helpers.ThreadLocalMap.childValue
Set breakpoint org.apache.log4j.helpers.ThreadLocalMap.childValue
>
Breakpoint hit: "thread=OrderServiceScheduler-thread-151389734", org.apache.log4j.helpers.ThreadLocalMap.childValue(), line=34 bci=0
OrderServiceScheduler-thread-151389734[1] clear org.apache.log4j.helpers.ThreadLocalMap.childValue
Removed: breakpoint org.apache.log4j.helpers.ThreadLocalMap.childValue
OrderServiceScheduler-thread-151389734[1] where
[1] org.apache.log4j.helpers.ThreadLocalMap.childValue (ThreadLocalMap.java:34)
[2] java.lang.ThreadLocal$ThreadLocalMap.<init> (ThreadLocal.java:353)
[3] java.lang.ThreadLocal$ThreadLocalMap.<init> (ThreadLocal.java:261)
[4] java.lang.ThreadLocal.createInheritedMap (ThreadLocal.java:236)
[5] java.lang.Thread.init (Thread.java:401)
[6] java.lang.Thread.<init> (Thread.java:652)
[7] com.kbcfp.util.NamedThreadFactory.newThread (NamedThreadFactory.java:45)
[8] java.util.concurrent.ThreadPoolExecutor$Worker.<init> (ThreadPoolExecutor.java:598)
[9] java.util.concurrent.ThreadPoolExecutor.addWorker (ThreadPoolExecutor.java:913)
[10] java.util.concurrent.ThreadPoolExecutor.processWorkerExit (ThreadPoolExecutor.java:992)
[11] java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1,128)
[12] java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:603)
[13] java.lang.Thread.run (Thread.java:722)
OrderServiceScheduler-thread-151389734[1] next
>
Step completed: "thread=OrderServiceScheduler-thread-151389734", org.apache.log4j.helpers.ThreadLocalMap.childValue(), line=35 bci=5
OrderServiceScheduler-thread-151389734[1] next
>
Step completed: "thread=OrderServiceScheduler-thread-151389734", org.apache.log4j.helpers.ThreadLocalMap.childValue(), line=38 bci=14
OrderServiceScheduler-thread-151389734[1] next
>
Step completed: "thread=OrderServiceScheduler-thread-151389734", com.kbcfp.util.NamedThreadFactory.newThread(), line=45 bci=40
OrderServiceScheduler-thread-151389734[1] next
>
Step completed: "thread=OrderServiceScheduler-thread-151389734", com.kbcfp.util.NamedThreadFactory.newThread(), line=48 bci=41
OrderServiceScheduler-thread-151389734[1] next
>
Step completed: "thread=OrderServiceScheduler-thread-151389734", com.kbcfp.util.NamedThreadFactory.newThread(), line=49 bci=49
OrderServiceScheduler-thread-151389734[1] next
>
Step completed: "thread=OrderServiceScheduler-thread-151389734", com.kbcfp.util.NamedThreadFactory.newThread(), line=52 bci=67
OrderServiceScheduler-thread-151389734[1] next
>
我们的NamedThreadFactory
课程没有做任何令人兴奋的事情:
44: public Thread newThread(Runnable r) {
45: Thread t = new Thread(_group, r,
46: _namePrefix + _threadNumber.getAndIncrement(),
47: 0);
48: t.setDaemon(_makeDaemon);
49: if(_overridePriority != null) {
50: t.setPriority(_overridePriority);
51: }
52: return t;
53: }
据我所知,下一行执行应该是ThreadPoolExecutor
的914,但线程会死掉。为什么呢?
作为参考,这是在Solaris x86主机上的JDK 1.7.0_07上运行的。
我所做的唯一改变是添加了ActiveMQ的客户端库。我怀疑这与在类路径中添加slf4j(特别是slf4j-api-1.6.6.jar
和slf4j-log4j12-1.6.6.jar
)有关,但我无法证明这一点。
更新
我已将执行的任务更改为单独的类:
public class TestingFoo implements Runnable
{
private final Logger s_log;
public TestingFoo(final Logger log)
{
s_log = log;
}
@Override
public void run()
{
try
{
s_log.info("Resetting order books");
// _liveOrderBook.clear();
}
catch (final Throwable t)
{
t.printStackTrace();
}
}
}
我在jdb
行设置了一个log.info
断点并且没有被击中。另外,我没有看到在stderr(或log4j文件)上打印堆栈跟踪。另外,我在安排任务之前放了另一条s_log.info
行, 打印出来。
我现在正在下载JDK 1.7.0_21以查看是否会产生影响。
更新2
这是由于将核心池大小设置为零,因为Peter Lawrey谈到了他的回答。但是,它不会导致无法运行,而是导致ThreadPoolExecutor
的{{1}}方法立即退出。该方法的要点如下:
runWorker
设置核心池大小会导致runWorker(Worker) {
...
while (task != null || (task = getTask()) != null) {
...
task.run();
...
}
processWorkerExit(..);
}
返回getTask
而不会阻止要提交的任务。这导致循环退出,然后调用null
,这将创建另一个线程来替换现有的线程。
我相信我在调试器中看到的问题是由于JIT的代码。我在线程工厂中放入一个processWorkerExit
语句来减慢线程的创建速度,这意味着在应用任何优化之前我有时间附加调试器。
TL; DR:我是个白痴。
更新3
对于未来可能会遇到这种情况的任何人......还有另一个similar question on SO提到这种行为在Java 7中发生了变化。在改变之前,核心大小为零,就像Peter Lawrey建议的那样,导致没有线程被创建(bug report)。放入change以确保创建一个线程,这似乎会导致重复创建线程。这可以使用与错误报告中的测试用例非常相似的代码进行复制:
Thread.sleep
答案 0 :(得分:2)
我误解了这个问题。你是建议线程本身被杀死并重新启动?您将核心大小设置为0,这不太可能达到您想要的效果。我希望它不会创建一个线程,在这种情况下你的线程永远不会运行,或者它会为每个任务创建一个线程(我怀疑不会)。
我根本不会设置核心大小而只使用Executors.newSingleThreadScheduledExecutor(ThreadFactory threadFactory)使用核心大小很少会按照我的经验完全按照您的意愿执行;)
如果这是问题,它应该在本地PC上执行完全相同的操作,您不需要进行远程调试。
您很可能会抛出一个被丢弃的错误。这很容易完成,是线程默默死亡的常见情况。我建议你遵循这种模式。
new Runnable() {
@Override public void run() {
try {
run0();
} catch(Throwable t) { // this will catch everything not just Exceptions.
// log t or print it
}
}
void run0() {
s_log.info("Resetting order books");
_liveOrderBook.clear();
}
}
您可以使用final run()方法定义AbstractRunnable以将其包装起来。只有在您不轮询Future返回以检查故障时才需要它。 (我怀疑你丢弃了这个对象)