Multithreading 消息驱动Bean线程安全JavaEE5与EE6中的并发性
我有一种情况,我需要将一组操作封装到单个事务中,并从MDB实现线程安全 如果线程A执行指令1,则不希望其他线程可以读取线程A正在处理的数据,至少不能读取相同的数据。 在下面的代码中,由于图像表包含来自不同来源的重复数据,这将导致重复违规。需要避免的情况 我找到的实际解决方案是为每个新消息声明一个新事务,并同步整个事务。 简化代码: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
@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()中输入时,我不希望其他线程可以读取映像表。我的问题更多的是从实施的角度提出的。不管怎样,你的回答让我通过了这个解决方案,所以我会接受这个解决方案。非常感谢。