这是两部分系列的第二篇。 第一部分介绍了有状态和无状态EJB的生命周期以及并发行为。 在这篇文章中,我将介绍Singleton EJB 。
Singleton模式可以说是最常用(有时被滥用!)的模式。
Java EE使我们无需编写显式代码(如上图所示)即可实现Singleton模式。
EJB 3.1本身就是Java EE 6的一部分,因此引入了Singleton EJB。
所需要的只是在一个豆类上提供一个@ javax.ejb.Singleton (类级别)注释(如果需要完善其他方面,还可以添加更多注释),以将其指定为Singleton会话bean。
JVM中只有一个实例和一个Singleton EJB实例 –无论有多少客户端访问它。 它不像有状态SB(一个在整个生命周期内附加到单个客户端的bean实例),也不像无状态SB(每个状态),每个客户端请求都有一个新实例。
Singleton Session Bean的生命周期中有哪些不同的状态?
Singleton Bean的生命周期与无状态会话Bean相同-实际上,这是此Bean类型的简单方面之一:
- 不存在
- 准备
状态如何变化? 是什么触发了他们?
这是一个快速的表格快照和一个高级图表。 。 。
国家过渡 | 扳机 | 回呼 |
---|---|---|
DNE转R | 首次通过JNDI / DI访问实例或由容器使用@Startup或@DependsOn自动实例化实例时 | @PostConstruct |
R到DNE | 容器关闭–销毁bean实例,或者@PostConstruct注释方法中发生异常 | @PreDestroy |
注意 :DNE –不存在, R –就绪
如前所述,生命周期是Singleton bean的较简单功能之一。 了解它们的并发方面至关重要。
Singleton Session Bean:并发管理
如前所述– Singleton在JVM中只有一个实例。 在Java EE环境中,并发访问是不可避免的–这就是为什么我们首先使用Java EE之类的技术的原因! 需要确保根据用例和需求,仔细考虑Singleton bean的并发( 锁定 )策略。
Singleton bean并发可以分为2个主要类别 :
- 容器托管(默认)
- Bean托管
容器管理并发
- 顾名思义,容器为Bean应用了明智的默认配置
- 可以使用注释和XML(部署描述符)进行控制
- 在bean类本身上使用@ javax.ejb.ConcurrencyManagement注释明确声明
- 默认值为javax.ejb.ConcurrencyManagementType.CONTAINER
- 容器提供了两种可能的锁定策略 –适用于bean类或其单个方法
- @ javax.ejb.Lock ,值为javax.ejb.LockType.READ-允许在没有写锁的情况下进行并发访问
- 可以在Bean类或方法上指定@ javax.ejb.AccessTimeout,以确保线程在不确定的时间段内不会阻塞或持有锁
Bean托管并发
- 该名称清楚地表明– Bean的并发方面留给开发人员。 与容器通过上述构造提供的并发控制相比,需要更好的并发控制是有意义的
- 需要使用适当的Java并发构造,例如同步,易失等
- 很难正确!
代码示例
让我们看一个简单的代码片段,以便更好地理解上述事实:
方案一 –容器管理的并发(默认,未明确指定锁定类型)
package com.abhirockzz.wordpress.ejb.lifecycle.singleton;import com.abhirockzz.wordpress.ejb.lifecycle.stateful.MyStatefulBean;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.Singleton;
import javax.ejb.Startup;@Singleton
@Startup
public class MySingletonBean {public void act() {System.out.println("Entered MySingletonBean/act() on " + new Date().toString() + " . Singleton instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());try {Thread.sleep(2000);} catch (InterruptedException ex) {Logger.getLogger(MyStatefulBean.class.getName()).log(Level.SEVERE, null, ex);}System.out.println("Exit MySingletonBean/act() on " + new Date().toString() + " . Singleton instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());}
}
package com.abhirockzz.wordpress.ejb.lifecycle.singleton;import java.io.IOException;
import java.util.Date;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@WebServlet(name = "SingletonTestServlet", urlPatterns = {"/SingletonTestServlet"})
public class SingletonTestServlet extends HttpServlet {public SingletonTestServlet() {}@InjectMySingletonBean mySingleton;@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {System.out.println("Entered SingletonTestServlet/doGet() on " + new Date().toString() + " . Servlet instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());mySingleton.act();}}
使用Apache JMeter –我在SingletonTestServlet触发了2个并发线程(是的,只有两个。这更多是演示,而不是负载测试竞赛!)
观察结果
查看日志,可以轻松得出以下几点:
- Servlet当然不是线程安全的,因此两个线程同时进入
- 其中一个线程在Singleton bean类中输入方法(标记为红色),并且由于容器强制使用默认的WRITE锁定类型 ,因此禁止进一步访问
- 第一个线程完成执行后,最初被阻塞的第二个线程(标记为绿色)就有机会执行Singleton bean方法
- 很简单!
方案二 –坚持容器管理的并发性。 将显式锁定类型从WRITE更改为READ
import com.abhirockzz.wordpress.ejb.lifecycle.stateful.MyStatefulBean;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.ConcurrencyManagement;
import javax.ejb.ConcurrencyManagementType;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.Singleton;
import javax.ejb.Startup;@Singleton
@Startup
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class MySingletonBean {@Lock(LockType.READ)public void act() {System.out.println("Entered MySingletonBean/act() on " + new Date().toString() + " . Singleton instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());try {Thread.sleep(2000);} catch (InterruptedException ex) {Logger.getLogger(MyStatefulBean.class.getName()).log(Level.SEVERE, null, ex);}System.out.println("Exit MySingletonBean/act() on " + new Date().toString() + " . Singleton instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());}
}
当应用程序被2个并发线程轰炸(双关!)时,会发生什么情况。 。 。 ?
- 如预期的那样,两个线程同时进入Servlet
- 线程之一进入Singleton bean类中的方法(标记为红色)
- 第二个线程(标记为绿色)也设法同时进入Singleton bean方法(检查时间戳记)
- 再次-非常简单!
我现在所描述的不是Bean管理并发。 如上所述,将BMC用于Singleton会将责任转移给开发人员,并且他可以自由地将并发功能编码到Bean中,这可以简单地在每种方法或其他机制(例如,从java.util.concurrent API)上使用同步来完成。
建议阅读
- EJB(3.2)规范
干杯!
翻译自: https://www.javacodegeeks.com/2014/09/ejb-3-x-lifecycle-and-concurrency-models-part-2.html