消息驱动 Bean 中的并发性 - 线程安全 Java EE5 与 EE6

Concurrency in Message Driven Bean - Thread safe Java EE5 vs. EE6

我遇到这样一种情况,我需要将一组操作封装到单个事务中,并且对 MDB 而言是线程安全的。

如果线程A执行了指令1,不希望其他线程可以读到,至少不一样,线程A正在处理的数据。 在下面的代码中,由于 IMAGE table 包含来自不同来源的重复数据,这将导致重复的 INFRANCTION。需要避免的情况。

我找到的实际解决方案是为每条新消息声明一个新事务并同步整个事务。 简化代码:

@Stateless
InfranctionBean{
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    checkInfranction(String plate){
        1. imageBean.getImage(plate); // read from table IMAGE
        2. infranctionBean.insertInfranction(String plate); // insert into table INFRANCTION
        3. imageBean.deleteImage(String plate); //delete from table IMAGE
    }
}

@MessageDriven
public class ImageReceiver {

    private static Object lock = new Object();

    public void onMessage(Message msg){
        String plate = msg.plate;

        synchronized (lock) {
            infanctionBean.checkInfranction(plate);
        }
    }
}

我知道 EJB 规范不推荐在 EJB 中使用同步块。如果应用程序服务器在双节点集群中运行,这甚至会导致问题。

貌似EE6针对这种场景引入了解决方案,就是EJB Singleton。 在这种情况下,我的解决方案是这样的:

@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
@Singleton
InfranctionBean{

    @Lock(LockType.WRITE)
    checkInfranction(String plate){
        1...
        2...
        3...
    }
}

并且从 MDB 中不需要使用同步块,因为容器将处理并发。 使用@Lock(WRITE),容器保证了 checkInfranction() 中单线程的访问。

我的问题是:如何在 EE5 中处理这种情况?有没有使用同步块的更干净的解决方案?

环境:Java5,jboss-4.2.3.GA,Oracle10.

实际解决方案

@Stateless
InfranctionBean{
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    checkInfranction(String plate){    
        1. imageBean.lockImageTable(); // lock table IMAGE in exclusive mode
        2. imageBean.getImage(plate); // read from table IMAGE
        3. infranctionBean.insertInfranction(String plate); // insert into table INFRANCTION
        4. imageBean.deleteImage(String plate); //delete from table IMAGE
    }
}

@MessageDriven
public class ImageReceiver {
    public void onMessage(Message msg){
        infanctionBean.checkInfranction(msg.plate);
    }
}

在 20.000 条传入消息(其中一半同时出现)上,应用程序似乎运行正常。

@Lock(WRITE) 只是单个 application/JVM 中的一个锁,因此除非您可以保证只有一个 application/JVM 正在访问数据,否则您无论如何都得不到太多保护。如果您只寻求单个 application/JVM 保护,EE 5 中的最佳解决方案是 ReadWriteLock 或同步块。 (EJB 规范有语言来劝阻应用程序不要这样做,以避免损害服务器的线程管理,因此请注意不要无限期地阻塞,不要忽略中断等)

如果您正在寻找更健壮的跨application/JVM 解决方案,我会使用数据库锁或隔离级别,而不是尝试依赖 JVM 同步原语。无论使用何种 EJB 版本,这都可能是最佳解决方案。