使用Java语言编写MQ应用程序,少不免要使用多线程来提高并发性能。但在get操作时如果选择没有消息就等待的话(即MQGetMessageOptions.options使用MQC.MQGMO_NO_WAIT之外的选项时),就要考虑线程阻塞的问题了。
原来的思想是这样的,在Queue Manager里有两条队列,不妨设为Q01与Q02,我们需要用多条线程去打开这两个队列取出消息。为了避免线程问题,多个线程共享同一个MQQueueManager实例(用Spring中的Scope=singleton指定),而在每个线程中使用accessQueue打开队列,再用一个while(true)不断取出队列中的消息。暂定各用2条线程分别取出Q01与Q02的消息。
但这时问题出现了,运行程序后,在MQ Explorer中查看队列的打开输入计数(即get操作打开队列的个数),原本Q01与Q02应各为两个,但实际上有时只有1个,有时没有,达到预期结果的次数很少。而且有时候明明队列里有消息,队列也已经打开了,但程序就是没有把消息取出来。
调试了比较久也没发现问题出在哪里,后来查看WebSphere MQ Using Java (csqzaw15)这份文档时,发现了以下的提示:
If you perform a get with wait, all other threads using the same MQQueueManager are blocked until the call completes. If you need multiple threads to access WebSphere MQ simultaneously, then each thread must create its own MQQueueManager object. |
这才找到了问题所在,原来我在Spring中指定了全部线程都使用同一个MQQueueManager实例,所以在某条线程get其中一个队列的消息时,如果队列中没有消息则它会阻塞,从而导致了其他3条线程都堵塞了。这时如果其他线程还没有执行accessQueue操作打开队列的话,MQ Explorer中就当然不会看到输入计数增加。若另一队列里有消息,也会因为线程被阻塞导致不能把消息取出。
在Spring配置文件中将MQQueueManager的Scope改成prototype,问题就解决了……