多线程java应用程序中的奇怪行为(没有锁的死锁?)

时间:2014-03-20 02:08:53

标签: java multithreading deadlock jena

尝试运行多个线程来收集HTTP连接中的数据时,我有一种奇怪的行为。

事实上,我正在尝试使用Jena语义Web框架处理几个SPARQL查询。

我的应用程序使用以下代码创建线程轮询:

final ExecutorService executor = Executors.newFixedThreadPool(X);

其中X是我选择的线程数。

因此,我在超过200个SPARQL端点上运行相同的查询。此过程可以在多线程环境中进行,因为每个端点(通常)是不同的服务器。每个端点的延迟应该允许我创建比我拥有的物理处理器数量大得多的线程。

使用大量线程时,我有一种奇怪的行为。如果我使用15个线程,系统将正常处理。但是,如果我使用超过30个线程,系统将停止工作。我的意思是,日志(带有异步appender的log4j)只是停止获取新消息,CPU使用率变为0%e不再发生。

我怀疑的第一件事是僵局。使用java instalation提供的“Java Visual VM”,我看到每个线程都继续运行甚至什么也没做!接下来要做的是线程转储。对于大多数线程,结果类似于以下内容:

pool-2-thread-100" #117 prio=5 os_prio=0 tid=0x000000000b61c800 nid=0x1e4c in Object.wait() [0x0000000022e0e000]
   java.lang.Thread.State: RUNNABLE
    at com.hp.hpl.jena.query.QueryFactory.create(QueryFactory.java:78)
    at com.hp.hpl.jena.query.QueryFactory.create(QueryFactory.java:52)
    at com.hp.hpl.jena.query.QueryFactory.create(QueryFactory.java:40)
    at com.hp.hpl.jena.query.QueryExecutionFactory.sparqlService(QueryExecutionFactory.java:347)
    at websemantics.UtilitiesSparql.getExecution(UtilitiesSparql.java:378)
    at websemantics.UtilitiesSparql.runSparqlQuery(UtilitiesSparql.java:410)
    at websemantics.UtilitiesSparql.runSparqlQuery(UtilitiesSparql.java:245)
    - locked <0x0000000081268d10> (a websemantics.model.Dataset)
    at websemantics.UtilitiesSparql$SparqlTask.call(UtilitiesSparql.java:75)
    at websemantics.UtilitiesSparql$SparqlTask.call(UtilitiesSparql.java:1)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

   Locked ownable synchronizers:
    - <0x0000000081273738> (a java.util.concurrent.ThreadPoolExecutor$Worker)

堆栈中间的锁只是保证每个端点一次只执行一个查询。在测试代​​码中它不会发生。如上面的代码所示,所有线程的状态都是“RUNNABLE”。

正如您所看到的,代码“锁定”了查询对象的创建(稍后将通过网络执行)。为了测试,我已经包含一个synchronized块,方法QueryFactory.create将被调用一次。在那种情况下,Jena代码的另一部分(我不认为它是负责的)块=(

整个情况对我来说太奇怪了,我认为这可能是一个JVM错误。我在JDK 1.7(蔚蓝机器上的Windows 8和Server 2012),JDK 1.8(今天!Windows 8机器)和OpenJDK 1.7(Ubuntu 13. *)上进行了测试,每个人都有相同的行为。所以我不认为这是一个JVM错误,可能是我的错误,但我可以看到我做错了什么。

任何人都知道JVM何时以这种方式工作?整件事让我抓狂!

非常感谢!

更新1:我正在按其他人的要求添加有关此问题的更多信息。

我再次运行该工具并在不同时间(13:03,13:04,13:08和13:15)进行了四次线程转储。每一个都与其他人非常相似:代码在一段时间后逐渐冻结。我已将文件上传到我的onedrive:http://1drv.ms/1nGihAF

大多数线程停在上面堆栈的同一点,但有些停在这里:

at com.hp.hpl.jena.rdf.model.ModelFactory.createDefaultModel(ModelFactory.java:140)
    at com.hp.hpl.jena.util.FileManager.loadModelWorker(FileManager.java:305)
    at com.hp.hpl.jena.util.FileManager.loadModel(FileManager.java:258)
    at websemantics.utils.UtilitiesJena.getModelInEverySyntax(UtilitiesJena.java:140)
    at websemantics.utils.UtilitiesJena.getOntologyByURLNonSilently(UtilitiesJena.java:121)
    at websemantics.utils.UtilitiesJena.getOntologyByURLNonSilently(UtilitiesJena.java:54)
    at websemantics.utils.UtilitiesJena.getOntologyByURL(UtilitiesJena.java:184)
    at websemantics.utils.UtilitiesSparql.runSparqlQueryOverDump(UtilitiesSparql.java:299)
    at websemantics.utils.UtilitiesSparql.runSparqlQuery(UtilitiesSparql.java:249)
    at websemantics.utils.UtilitiesSparql$SparqlTask.call(UtilitiesSparql.java:74)
    at websemantics.utils.UtilitiesSparql$SparqlTask.call(UtilitiesSparql.java:41)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

我使用的Jena版本是2.10.0 ,因为我对maven的依赖显示:

<dependency>
    <groupId>org.apache.jena</groupId>
    <artifactId>apache-jena-libs</artifactId>
    <type>pom</type>
    <version>2.10.0</version>
</dependency>

第一个堆栈上QueryFactory的第78行是以下(在箭头 - &gt;处):

   static public Query create(String queryString, String baseURI, Syntax syntax)
   {
78->Query query = new Query() ;
       return parse(query, queryString, baseURI, syntax) ;
   }

第二个堆栈上ModelFactory的第144行是:

public static Model createDefaultModel()
        144->{ return new ModelCom( Factory.createGraphMem( ) ); }

就我所见,以下所有调用都没有任何同步块。

1 个答案:

答案 0 :(得分:1)

我没有在Jena中看到任何可能导致您描述的死锁行为类型的明显事件。首先是Jena的版本是什么?

第78行的

QueryFactory.create()只是创建一个对象的新实例,所以你可能会遇到JVM的GC问题,尽管通常情况下我希望看到非零CPU使用情况。当你完成它们时,通过调用QueryExecution来提高堆大小并检查你是否正确处理close()等资源可能有所帮助。

您已经提到,如果您synchronize QueryFactory.create()来电,那么您会在其他地方看到死锁,提供这些信息有助于诊断您的问题。在你的答案中发布一个完整的线程转储也很有用,所以我们可以看到每个线程所做的不仅仅是一个线程。

请注意,对Jena的并发查询数量不应有任何硬性限制。在我的雇主,我们经常运行使用Jena的测试工具,并愉快地运行100个并发查询请求进行压力测试。

修改

所以我认为问题是静态类初始化器中有synchronized块,请参阅1315转储中的这个特定线程:

"pool-1-thread-62" prio=6 tid=0x0000000029cf7000 nid=0xcbc in Object.wait() [0x000000002f9cd000]
   java.lang.Thread.State: RUNNABLE
    at org.apache.jena.riot.RIOT.<clinit>(RIOT.java:38)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:190)
    at com.hp.hpl.jena.rdf.model.impl.IO_Ctl.callByRefection(IO_Ctl.java:54)
    at com.hp.hpl.jena.rdf.model.impl.IO_Ctl.init(IO_Ctl.java:36)
    - locked <0x00000006e5bda610> (a java.lang.Object)
    at com.hp.hpl.jena.rdf.model.impl.ModelCom.<clinit>(ModelCom.java:65)
    at com.hp.hpl.jena.rdf.model.ModelFactory.createDefaultModel(ModelFactory.java:140)
    at com.hp.hpl.jena.util.FileManager.loadModelWorker(FileManager.java:305)
    at com.hp.hpl.jena.util.FileManager.loadModel(FileManager.java:258)
    at websemantics.utils.UtilitiesJena.getModelInEverySyntax(UtilitiesJena.java:140)
    at websemantics.utils.UtilitiesJena.getOntologyByURLNonSilently(UtilitiesJena.java:121)
    at websemantics.utils.UtilitiesJena.getOntologyByURLNonSilently(UtilitiesJena.java:54)
    at websemantics.utils.UtilitiesJena.getOntologyByURL(UtilitiesJena.java:184)
    at websemantics.utils.UtilitiesSparql.runSparqlQueryOverDump(UtilitiesSparql.java:299)
    at websemantics.utils.UtilitiesSparql.runSparqlQuery(UtilitiesSparql.java:249)
    at websemantics.utils.UtilitiesSparql$SparqlTask.call(UtilitiesSparql.java:74)
    at websemantics.utils.UtilitiesSparql$SparqlTask.call(UtilitiesSparql.java:41)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

ModelCom有一个静态类初始化程序,它调用IO_Ctl.init()调用具有静态类初始化程序的RIOT。当然,在过去,我知道我们已经有错误报告静态初始化链在哪里进入循环引用会导致死锁,或者事情会以错误的顺序初始化并相互破坏。

我知道我们已修复了这方面的错误,因此升级到当前稳定的2.11.1版本,因此从2013年2月的Jena 2.10.0升级到2014年1月的2.11.1可以解决问题。

您可以尝试的其他可能的解决方法是在代码变为多线程之前自己明确地调用相关的初始化程序,例如

ARQ.init();
RIOT.init();