Java 防止消息处理的竞争条件

Java 防止消息处理的竞争条件,java,jakarta-ee,Java,Jakarta Ee,我有一个J2EE应用程序,它通过web服务接收消息(事件)。消息的类型不同(根据类型需要不同的处理),并按特定顺序发送。它发现了一个问题,即某些消息类型比其他消息类型需要更长的处理时间。结果是,序列中第二个接收到的消息可以在序列中第一个消息之前处理。我试图通过在处理消息的方法周围放置一个同步块来解决这个问题。这似乎有效,但我不相信这是“正确”的方法?是否有更合适的替代方案,或者这是“可接受的”?我加入了一小段代码,试图更清楚地解释。。。。任何建议/指导都将不胜感激 public class Ev

我有一个J2EE应用程序,它通过web服务接收消息(事件)。消息的类型不同(根据类型需要不同的处理),并按特定顺序发送。它发现了一个问题,即某些消息类型比其他消息类型需要更长的处理时间。结果是,序列中第二个接收到的消息可以在序列中第一个消息之前处理。我试图通过在处理消息的方法周围放置一个同步块来解决这个问题。这似乎有效,但我不相信这是“正确”的方法?是否有更合适的替代方案,或者这是“可接受的”?我加入了一小段代码,试图更清楚地解释。。。。任何建议/指导都将不胜感激

public class EventServiceImpl implements EventService {
  public String submit (String msg) {

    if (msg == null)
        return ("NAK");

            EventQueue.getInstance().submit(msg);

    return "ACK";
  }
}


public class EventQueue {
    private static EventQueue instance = null;
    private static int QUEUE_LENGTH = 10000;
    protected boolean done = false;
    BlockingQueue<String> myQueue = new LinkedBlockingQueue<String>(QUEUE_LENGTH);

protected EventQueue() {
    new Thread(new Consumer(myQueue)).start();
}

public static EventQueue getInstance() {
      if(instance == null) {
         instance = new EventQueue();
      }
      return instance;
}

public void submit(String event) {
    try {
        myQueue.put(event);
    } catch (InterruptedException ex) {
    }
}

class Consumer implements Runnable {
    protected BlockingQueue<String> queue;

    Consumer(BlockingQueue<String> theQueue) { this.queue = theQueue; }

    public void run() {
      try {
        while (true) {
          Object obj = queue.take();
          process(obj);
          if (done) {
            return;
          }
        }
      } catch (InterruptedException ex) {
      }
    }

    void process(Object obj) {
        Event event = new Event( (String) obj);
        EventHandler handler = EventHandlerFactory.getInstance(event);
        handler.execute();
    }
}

// Close queue gracefully
public void close() {
    this.done = true;
}
public类eventserviceinpl实现EventService{
公共字符串提交(字符串消息){
如果(msg==null)
回报(“NAK”);
EventQueue.getInstance().submit(msg);
返回“确认”;
}
}
公共类事件队列{
私有静态EventQueue实例=null;
私有静态int队列长度=10000;
保护布尔完成=假;
BlockingQueue myQueue=新链接的BlockingQueue(队列长度);
受保护的事件队列(){
新线程(新使用者(myQueue)).start();
}
公共静态事件队列getInstance(){
if(实例==null){
instance=neweventqueue();
}
返回实例;
}
公共作废提交(字符串事件){
试一试{
myQueue.put(事件);
}捕获(中断异常例外){
}
}
类使用者实现Runnable{
受保护的阻塞队列;
消费者(阻止队列队列){this.queue=theQueue;}
公开募捐{
试一试{
while(true){
Object obj=queue.take();
过程(obj);
如果(完成){
返回;
}
}
}捕获(中断异常例外){
}
}
作废处理(对象obj){
事件=新事件((字符串)obj);
EventHandler=EventHandlerFactory.getInstance(事件);
handler.execute();
}
}
//优雅地排队
公众假期结束(){
this.done=true;
}

如果事件要按特定顺序处理,那么为什么不尝试将“eventID”和“orderID”字段添加到消息中?这样,EventServiceImpl类就可以按正确的顺序进行排序、排序和执行(无论创建和/或传递给处理程序的顺序如何)

同步
handler.execute()
块将不会得到期望的结果,我预计。
synchronized
关键字所做的只是防止多个线程同时执行该块。它在正确排序下一个线程方面没有任何作用

如果
synchronized
块确实能让事情顺利进行,那么我断言您是非常幸运的,因为消息是按照正确的顺序创建、传递和执行的。在多线程环境中,这是不确定的!我会采取措施确保您能够控制它,而不是依靠运气

例如:

  • 消息按“client01-A”、“client01-C”的顺序创建, “client01-B”、“client01-D”
  • 消息以“client01-D”的顺序到达处理程序, “client01-B”、“client01-A”、“client01-C”
  • EventHandler可以将消息从一个客户端分离到另一个客户端,并开始缓存“client01”的消息
  • EventHandler recv的“client01-A”消息,并且知道它可以处理此消息,并且这样做
  • EventHandler在缓存中查找消息“client01-B”,找到并处理它
  • EventHandler找不到“client01-C”,因为它尚未到达
  • EventHandler recv的“client01-C”并对其进行处理
  • EventHandler在缓存中查找“client01-D”,找到它,处理它,并认为“client01”交互已完成

  • 这样做可以确保正确的处理并促进多线程的良好使用。

    我不确定您正在使用的框架(EJB(MDB)/JMS)是什么。通常应该避免在EJB/JMS这样的托管环境中使用同步(这不是一个好的实践)。一种解决方法是

    • 客户端应该等待服务器的确认,然后再发送下一条消息
    • 这样,客户机本身就可以控制事件的顺序
    请注意,如果有多个客户端提交消息,这将不起作用

    编辑:

    在这种情况下,web服务的客户端按顺序发送消息,而不考虑消息处理时间。它只是一个接一个地转储消息。这是一个基于web服务的解决方案的好例子。我建议以下两种方法来实现这一点

  • 使用。这将增加额外的开销,包括添加和编写一些管道代码

  • 使用一些多标题模式,如web服务处理程序将在队列中转储传入消息,而单线程使用者将一次消费一条消息。请参阅使用java.util.concurrent包

  • 使用数据库。将传入消息转储到数据库中。使用不同的基于调度程序的程序扫描数据库(基于序列号),并相应地处理消息

    第一种和第三种解决方案对于这些类型的问题来说是非常标准的。第二种方法速度很快,不需要在代码中添加任何库


  • 在这种情况下,我对发送消息的系统没有任何访问/控制权,尽管每条消息都包含一个序列号。我使用cxf构建了接收消息的web服务。您有多个客户端以其自己的消息序列发送消息?该服务只有一个客户端。实际上