概述
我们通常使用 JMS session 连接JMS消息队列,创建生产者往队列发送消息,或创建消费者从队列消费消息,BaseJMSTaskServer.java中的start()方法就是一个例子,注意BaseJMSTaskServer.java使用到JMS 相关的API如下:
import javax.jms.MessageConsumer;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSession;
import javax.jms.Session;
根据JMS标准的设计,这些 API 它是设计为单个线程使用,我们必须在单个线程中创建使用并关闭它们。误区是好多人在工作中忽略掉这点,创建 JMS session 后被多个线程并发使用,这种使用是错误的。我先摘出 JMS specifications 中相关定义如下,接这举两个典型的例子,以及如何避免误区。
JMS specifications
JMS session objects are single threaded and can only be used by single thread that created them. An attempt to use them from another thread would result in an exception or some very strange errors.
典型例子一
jBPM 提供通过JMS的方式与Task Service交互(深入理解 jBPM Human Task - 使用JMS做传输媒介执行Human Task),JMSTaskClientConnector.java用来供第三方程序使用,好多人设计JMSTaskClientConnector实例为单例模式,多个线程中并发使用JMSTaskClientConnector,结果出现错误导致jBPM不能够正常工作。
典型例子二
Spring 提供对JMS的封装spring-jms,其中好多人对JmsTemplate使用也是有误区的,常见的错误如下:
for(;;){
...
.getJmsExecuteTemplate().sendQueueString("XXXX");
...
}
这样也导致并发使用JMS session现象发生,使得应用异常发生。
避免误区
javax.jms.ServerSessionPool接口是为JEE容器提供,JEE容器实现了该接口,该节口考虑的是并发多线程场景,例如部署消息驱动Bean到JEE容器就使用的是ServerSessionPool实现,它考虑到线程安全性等。
所以如何避免误区很简单,将你的并发请求交给JEE容器处理,或者自己实现javax.jms.ServerSessionPool,来完成线程安全等的考虑。