我正在尝试使用新的 Concurrency API 来注入托管线程工厂 http://docs.oracle.com/javaee/7/api/javax/enterprise/concurrent/ManagedThreadFactory.html并使用它Oracle 教程 http://docs.oracle.com/javaee/7/tutorial/doc/concurrency-utilities006.htm.
这是我正在谈论的一个例子:
@Singleton
@Startup
public class Demo {
@Resource(name="concurrent/__DefaultManagedThreadFactory") ManagedThreadFactory threadFactory;
@PostConstruct
public void startup() {
threadFactory.newThread(
new Runnable() {
@Override
public void run() {
System.out.println("Do something.");
}
}
).start();
}
}
我正在使用 Glassfish 插件在 Eclipse 中进行开发。当我进行更改后重新发布时,我总是在服务器日志中看到这一行。我们每次调用 start() 时它都会出现一次:
SEVERE: java.lang.IllegalStateException: Module (my application) is disabled
它实际上并没有抛出 IllegalStateException,只是报告已在 Glassfish 中抛出(并捕获)一个 IllegalStateException。应用程序正常部署,但没有线程启动。如果我随后重新发布并第二次,“错误”就会消失并且线程会按预期启动。
当我尝试将应用程序部署到“真正的”Glassfish 设置(没有 Eclipse)时,它总是报告部署成功,并且日志不包含“错误”。但它仍然不会启动线程(即使重复部署)。
我是否正确使用了并发 API?会不会是配置问题?根据记录,如果我使用 ManagedExcecutorService ,我会得到相同的行为。
根据记录,这个问题是几个月前在这里提出的:我可以在 Singleton Enterprise Java Bean 中启动 ManagedThread 吗? https://stackoverflow.com/questions/20446682/can-i-start-a-managedthread-in-a-singleton-enterprise-java-bean,但它并没有得到真正的答复,而且我除了再次询问之外还没有做任何事情的声誉。对不起!
UPDATE: 这个答案 https://stackoverflow.com/a/28360042/3593206 by 佩阿克塞尔·费尔斯 https://stackoverflow.com/users/1806345/per-axel-felth作品。谢谢你!我对该解决方案进行了一些重构,以尝试将解决方法代码与我的原始应用程序逻辑隔离:
@Singleton
@Startup
public class Demo {
@Resource(name="java:comp/DefaultManagedThreadFactory") ManagedThreadFactory threadFactory;
@EJB private ConcurrencyInitializer concurrencyInitializer;
@EJB private Demo self;
@PostConstruct
public void startup() {
self.startThread();
}
@Asynchronous
public void startThread() {
//This line applies the workaround
concurrencyInitializer.init();
//Everything beyond this point is my original application logic
threadFactory.newThread(
new Runnable() {
@Override
public void run() {
System.out.println("Do something.");
}
}
).start();
}
}
/**
* A utility class used to get around a bug in Glassfish that allows
* Concurrency resources (ManagedThreadFactory, ManagedExecutorService, etc)
* to be injected before they are ready to be used.
*
* Derived from solution by Per-Axel Felth in: https://stackoverflow.com/questions/23900826/glassfish-4-using-concurrency-api-to-create-managed-threads
*/
@Singleton
public class ConcurrencyInitializer {
/**
* The number of milliseconds to wait before try to
*/
public static final long RETRY_DELAY = 500L;
/**
* The maximum number of concurrency attempts to make before failing
*/
public static final int MAX_RETRIES = 20;
/**
* Repeatedly attempts to submit a Runnable task to an injected ManagedExecutorService
* to trigger the readying of the Concurrency resources.
*
* @return true if successful (Concurrency resources are now ready for use),
* false if timed out instead
*/
public boolean init() {
final AtomicBoolean done = new AtomicBoolean(false);
int i = 0;
try {
while (!done.get() && i++ < MAX_RETRIES) {
executorService.submit(new Runnable() {
@Override
public void run() {
done.set(true);
}
});
Thread.sleep(RETRY_DELAY);
}
} catch(InterruptedException e) {
//Do nothing.
}
return done.get();
}
}