我有一个执行后台任务的Singleton Session Bean:
@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
@TransactionManagement(TransactionManagementType.BEAN)
@Startup
public class TaskQueue {
private static final Logger LOGGER = Logger.getLogger("TaskQueue");
@Resource
private SessionContext sessionContext;
private final ArrayList<Runnable> tasks = new ArrayList<Runnable>();
private boolean running = false;
@PostConstruct
public void postConstruct() {
LOGGER.info("postConstruct");
running = true;
sessionContext.getBusinessObject(TaskQueue.class).taskLoop();
}
@PreDestroy
public void preDestroy() {
LOGGER.info("preDestroy");
running = false;
synchronized (tasks) {
tasks.notifyAll();
}
}
public void addTask(Runnable r) {
synchronized (tasks) {
tasks.add(r);
tasks.notifyAll();
}
}
@Asynchronous
public void taskLoop() {
while (running) {
Runnable task;
synchronized (tasks) {
LOGGER.info("Fetching next task...");
if (tasks.isEmpty()) {
try {
LOGGER.info("Task queue is empty. Waiting...");
tasks.wait();
LOGGER.info("Resumed");
continue;
} catch (InterruptedException e) {
break;
}
}
task = tasks.remove(0);
}
LOGGER.info("Executing task...");
task.run();
}
running = false;
LOGGER.info("Task queue exited");
}
}
当我尝试停止模块,取消部署模块或停止服务器时,preDestroy()
方法未被调用,停止/取消部署过程不会继续。停止服务器的唯一方法是终止Java进程。
我使用的是Jboss EAP 6.0。
我的代码出了什么问题?如何解决它,或者使用EJB 3.1进行后台任务队列处理的替代方法是什么?
答案 0 :(得分:1)
首先,EE规范禁止管理自己的线程。你可以这样做但你应该知道你违反了规范并理解了所有固有的含义。相反,您应该考虑利用Managed Executor服务[1]。
据说我怀疑这里发生的是你的taskLoop是锁定对其他单身人士(包括预先破坏)方法的访问。默认情况下,@ Singleton上的所有方法都是@Lock LockType.Write。由于您已经手动同步,因此您应该尝试使用@Lock(LockType.Read)注释@Singleton类[2]
[1] https://docs.oracle.com/javaee/7/api/javax/enterprise/concurrent/ManagedExecutorService.html
[2] https://docs.oracle.com/javaee/6/api/javax/ejb/LockType.html