我将Java EE @Stateless
-bean注入到@Singleton
/ @ApplicationScoped
-bean中:
@Stateless
public class MyStateless {
@PersistenceContext
private EntityManager em;
public void persist(User u){
// for each thread the emProxy is the same
log.info("emProxy={0}", em.toString());
// for each thread the emDelegate is differently
log.info("emDelegate={0}", em.getDelegate().toString());
// this is definitly thread-safe !
em.persist(u);
}
}
版本1:
@javax.ejb.Singleton
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
stateless.persist(new User("hello"));
}
}
版本2:
@ApplicationScoped
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
stateless.persist(new User("hello"));
}
}
版本3:
@javax.ejb.Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class MySingleton{
@Inject
private MyStateless stateless;
public void persistUser(){
// is this hread safe ?
stateless.persist(new User("hello"));
}
}
我有以下声明:
“如果我将@Stateless
-bean注入任何类型的@Singleton
或@ApplicationScoped
-bean中,那么对于每次调用persistUser()
,容器池将提供MyStateless
的另一个(不一定相同)实例,因此@Stateless
-bean与@Singleton
/ {{1之间不存在1:1关系}}-bean,这也意味着,容器@ApplicationScoped
被注入到我的entiyManager
-bean中,该容器由我的@Stateless
/ @Singleton
-bean通过{ {1}}是线程安全的。”
上述声明是否对所有3个版本正确,或者它们与我的无状态bean的行为是否不同?
并且请查看以下特殊情况(版本4):
@ApplicationScoped
第4版线程安全吗?必须这样做,因为我使用的是persistUser()
的{{1}} Bean中的方法,并且按照定义,是由容器管理的@javax.ejb.Singleton
public class MySingleton {
@Resource private ManagedScheduledExecutorService managedExecutor;
@Inject
private MyStateless stateless;
public void persistUser(){
// is this thread safe?
for(int i=0; i<=4;i++){
managedExecutor.scheduleWithFixedDelay(()
-> stateless.persist(new User("hello_" + i)) , 0, 5, TimeUnit.SECONDS);
}
}
}
stateless
Bean中的变量必须是线程安全的。我说的对吗?
查看日志,每个线程打印相同的Runnable
-proxy:
entityManager
但每个踏步的委托是不同的(委托给基础持久性提供程序休眠):
stateless
entityManager
的 proxy 在每个线程上都是相同的,但是每个线程上的emProxy = org.jboss.as.jpa.container.TransactionScopedEntityManager@3297c304
-proxy的 delegate 是不同的,因此,它是线程安全的。我对吗?还是每个线程都需要不同的emDelegate = SessionImpl(2028358710<open>)
代理才能确保线程安全?
顺便说一句,如果我将entityManager
更改为entityManager
entityManager
如果我使用MyStateless
,则所有同时运行的线程都具有相等的@ApplicationScoped
-代理,但是具有不同的@ApplicationScoped
public class MyStateless {..}
-代理。因此,我看不出有任何理由选择@ApplicationScoped
而不是entityManager
。
结论:
在上述情况下,没有问题:
“只要代理不同,相同的代理就可以用于同时运行的线程。”这些代理是什么(entityManager,无状态Bean等,都没有关系。)
答案 0 :(得分:1)
根据JPA规范(2.1版,第7.2章):
不得在多个同时执行的线程之间共享实体管理器,因为实体管理器和持久性上下文不需要是线程安全的。实体管理器只能以单线程方式访问。
MyStateless
bean可以被许多客户端使用,因为应用服务器负责注入的EntityManager
。因此MyStateless
对外部世界是线程安全的;从容器内手动产生的线程(即未使用应用服务器的工具(如EntityManager
创建的线程)访问其ManagedExecutorService
是不安全的。我什至不能确定,即使从容器管理的线程使用相同的EntityManager
也是安全的。从多个客户端对应的线程并发使用它当然是安全的。另请参见EJB规范(版本3.2,第4.8.5章):
在单例会话bean实例状态中存储不支持并发访问(例如,对Java Persistence实体管理器或有状态会话bean的引用)的Java EE对象是合法的。但是,Bean Provider的责任是确保一次不能由多个线程访问此类对象。
请记住,只要手动生成的线程中没有发生访问MyStateless
的版本1、2、3即可。仅因为(a)线程是由容器管理的,所以版本4可以保持原样,因为它们将确保每个容器使用不同的EntityManager
和(b)工作负载每个线程的线程都是独立的-可以在不同的事务中运行。
实际上,注入“客户端”与注入的MyStateless
Bean之间没有1-1的关系。实际上,容器应该只为每个注入点注入一个代理,并且代理负责解析要使用的适当实例。