Multithreading 消息驱动Bean线程安全JavaEE5与EE6中的并发性

Multithreading 消息驱动Bean线程安全JavaEE5与EE6中的并发性,multithreading,thread-safety,ejb,message-driven-bean,synchronize,Multithreading,Thread Safety,Ejb,Message Driven Bean,Synchronize,我有一种情况,我需要将一组操作封装到单个事务中,并从MDB实现线程安全 如果线程A执行指令1,则不希望其他线程可以读取线程A正在处理的数据,至少不能读取相同的数据。 在下面的代码中,由于图像表包含来自不同来源的重复数据,这将导致重复违规。需要避免的情况 我找到的实际解决方案是为每个新消息声明一个新事务,并同步整个事务。 简化代码: @Stateless InfranctionBean{ @TransactionAttribute(TransactionAttributeType.REQU

我有一种情况,我需要将一组操作封装到单个事务中,并从MDB实现线程安全

如果线程A执行指令1,则不希望其他线程可以读取线程A正在处理的数据,至少不能读取相同的数据。 在下面的代码中,由于图像表包含来自不同来源的重复数据,这将导致重复违规。需要避免的情况

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

@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内部使用同步块。如果applicaton服务器在两节点集群中运行,这甚至会导致问题

似乎EE6已经为这个场景引入了一个解决方案,即EJB单例。 在这种情况下,我的解决方案如下:

@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);
    }
}
对于20000条传入消息(其中一半同时发送),应用程序似乎工作正常

@Lock(WRITE)
只是单个应用程序/JVM中的一个锁,因此除非您能够保证只有一个应用程序/JVM访问数据,否则您不会得到太多保护。如果您只寻找单个应用程序/JVM保护,那么EE5中的最佳解决方案将是读写锁或同步块。(EJB规范中有阻止应用程序这样做的语言,以避免损害服务器的线程管理,因此请注意不要无限期地阻塞,不要忽略中断等。)


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

感谢Brett的回复。数据库锁是什么意思?你的意思是,作为原子块的第一步,在EXCUSIVE模式下执行类似于
锁定表映像的操作?我不是什么数据库专家,但我知道两种常用的方法。首先,可以使用类似于在锁对象上同步的数据库锁。或者,您可以使用诸如read committed、repeatable reads或serializable之类的强隔离级别,这基本上会导致数据库根据两个线程读取/更新数据的方式隐式锁定,以确保它们不会读取另一个线程中由不同事务更新的数据。隔离级别是可序列化的,所以当在checkInfranction()中输入时,我不希望其他线程可以读取映像表。我的问题更多的是从实施的角度提出的。不管怎样,你的回答让我通过了这个解决方案,所以我会接受这个解决方案。非常感谢。