有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java阻止消息处理的竞争条件

我有一个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;
}

共 (2) 个答案

  1. # 1 楼答案

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

    • 客户端应该等待服务器的确认,然后再发送下一条消息
    • 这样,客户机本身就可以控制事件的顺序

    请注意,如果有多个客户端提交消息,这将不起作用

    编辑:

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

    1. 使用JMS。这将增加添加JMS providers和编写一些管道代码的额外开销

    2. 使用一些多标题模式,如Producer-Consumer,其中web服务处理程序将在队列中转储传入消息,单线程使用者将一次消费一条消息。请参阅this example使用java。util。并发包

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

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

  2. # 2 楼答案

    如果事件是按特定顺序处理的,那么为什么不尝试向消息中添加“eventID”和“orderID”字段呢?通过这种方式,EventServiceImpl类可以按照正确的顺序进行排序、排序和执行(无论它们是以何种顺序创建和/或交付给处理程序的)

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

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

    例如:

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

    沿着这些思路,可以确保正确的处理,并促进多线程的良好使用