单例模式——java设计模式
单例模式
目录:
一、何为单例
二、使用Java EE实现单例模式
三、使用场景
一、何为单例
确保一个类只有一个实例,并且提供了实例的一个全局访问点
**1.1 单例模式类图 **

1.2 单例模式实现
(1)简单实现
public class MySingleton1 {
private static MySingleton1 instance;
private MySingleton1() {
}
public static MySingleton1 getInstance() {
if (instance == null) { // 1
instance = new MySingleton1();
}
return instance;
}
}
(2)线程安全的单例模式
要解决竞态条件问题,你就需要获得一把锁,并且在实例返回后才释放。
public class MySingleton2 {
private static MySingleton2 instance;
private MySingleton2() {
}
public static synchronized MySingleton2 getInstance() {
if (instance == null) {
instance = new MySingleton2();
}
return instance;
}
}
(3)类加载时创建单例对象
这样不必同步单例实例的创建,并在JVM加载完所有类时就创建好单例对象
public class MySingleton3 {
private final static MySingleton3 instance = new MySingleton3();
private MySingleton3() {
}
public static MySingleton3 getInstance() {
return instance;
}
}
(4)静态块中的单例
这会导致延迟初始化,因为静态块是在构造方法调用前执行的。
public class MySingleton4 {
private static MySingleton4 instance = null;
static {
instance = new MySingleton4();
}
private MySingleton4() {
}
public static MySingleton4 getInstance() {
return instance;
}
}
(5)双重检测锁
双重检测锁比其他方法更加安全,因为它会在锁定单例类之前检查一次单例的创建,在对象创造前再一次检查
public class MySingleton6 {
private volatile MySingleton6 instance;
private MySingleton6() {
}
public MySingleton6 getInstance() {
if (instance == null) { // 1
synchronized (MySingleton6.class) {
if (instance == null) { // 2
instance = new MySingleton6();
}
}
}
return instance;
}
}
(6)枚举类型的单例模式
上面的方法都不是绝对安全的,如果开发者讲Java Reflection API的访问修饰符改为public,就可以创建单例了。Java中创建单例最佳方式是枚举类型。
枚举类型本质上就是单例的,因此JVM会处理创建单例所需的大部分工作。这样,通过使用枚举类型,就无需再处理同步对象创建与提供等工作了,还能避免与初始化相关的问题。
public enum MySingletonEnum {
INSTANCE;
public void doSomethingInteresting() {
}
}
在该示例中,对单例对象示例的引用是通过以下方式获得的:
MySingletonEnum mse=MySingletonEnum.INSTANCE;
一旦拥有了单例的引用,你就可以向下面这样调用它的任何方法了:
mse.doSomeThingInteresting();
二、使用Java EE实现单例模式
Java EE中可以使用上面的方法,但是还有一种更加优雅且易于使用的方式:单例Bean
1.单例Bean
只需将注解@Singleton添加到类上就可以将其转换为单例Bean
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import java.util.logging.Logger;
@Singleton
public class CacheSingletonBean8 {
private Map<Integer, String> myCache;
@PostConstruct
public void start() {
Logger.getLogger("MyGlobalLogger").info("Started!");
myCache = new HashMap<Integer, String>();
}
public void addUser(Integer id, String name) {
myCache.put(id, name);
}
public String getName(Integer id) {
return myCache.get(id);
}
}
通过注解的简单使用,Java EE不必配置XML文件。项目中有一个beans.xml文件,不过大多数时候其内容都是空的。你只是在启动上下文与依赖注入(CDI)容器时才需要使用它。@Singleton注解将类标记为一个单例EJB,容器会处理该单例实例的创建与使用。
2.在启动时使用单例
默认情况下,Java EE的单例是延迟初始化的,只在需要实例时并且是首次访问时才创建它。不过,你可能希望在启动时就创建实例,不需要任何延迟即可访问到单例,特别是创建实例的代价很大或是在容器启动时就需要Bean。要确保创建时就启动,可在类上使用@Startup注解。
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import java.util.logging.Logger;
@Startup
@Singleton
public class CacheSingletonBean9 {
private Map<Integer, String> myCache;
@PostConstruct
public void start() {
Logger.getLogger("MyGlobalLogger").info("Started!");
myCache = new HashMap<Integer, String>();
}
public void addUser(Integer id, String name) {
myCache.put(id, name);
}
public String getName(Integer id) {
return myCache.get(id);
}
}
3.确定启动顺序
但是,如果单例依赖于其他资源怎么办?比如:如果连接池是由另一个单例创建的会怎么样,或者日志依赖于另一个单例呢?Java EE提供了一个简单的注解来解决这个问题。使用@DependsOn注解,并将该类所以来的Bean的名字传递给它。
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.ejb.ConcurrencyManagement;
import javax.ejb.ConcurrencyManagementType;
import javax.ejb.DependsOn;
import javax.ejb.EJB;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.Singleton;
import javax.ejb.Startup;
@Startup
@DependsOn("MyLoggingBean") //加上此注解
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
@Singleton
public class CacheSingletonBean12 {
private Map<Integer, String> myCache;
@EJB
MyLoggingBean loggingBean;
@PostConstruct
public void start() {
loggingBean.logInfo("Started!");
myCache = new HashMap<Integer, String>();
}
@Lock(LockType.WRITE)
public void addUser(Integer id, String name) {
myCache.put(id, name);
}
@Lock(LockType.READ)
public String getName(Integer id) {
return myCache.get(id);
}
}
接下来再创建一个单例Bean,作为上一个Bean所用的Bean
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import java.util.logging.Logger;
@Startup
@Singleton
public class MyLoggingBean {
private Logger logger;
@PostConstruct
public void start() {
logger = Logger.getLogger("MyGlobalLogger");
logger.info("Well, I started first!!!");
}
public void logInfo(String msg) {
logger.info(msg);
}
}
4.管理并发
Java Ee提供了两种并发管理:容器管理并发与Bean管理并发。在容器管理并发中,容器负责处理读写访问相关的一切事宜,而Bean管理并发则需要开发者使用同步等传统的Java方法来处理并发。
可以通过ConcurrencyManagementType.BEAN注解管理并发。
默认情况下,Java EE使用的事容器管理并发,不过可以通过ConcurrentManagementType.CONTAINER注解进行显示声明。
@Startup
@DependsOn("MyLoggingBean")
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
@Singleton
@AccessTimeout(value = 120000)
// default in milliseconds
public class CacheSingletonBean13 {
回到之前的示例,使用@Lock注解来控制访问
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.ejb.ConcurrencyManagement;
import javax.ejb.ConcurrencyManagementType;
import javax.ejb.DependsOn;
import javax.ejb.EJB;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.Singleton;
import javax.ejb.Startup;
@Startup
@DependsOn("MyLoggingBean")
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
@Singleton
public class CacheSingletonBean12 {
private Map<Integer, String> myCache;
@EJB
MyLoggingBean loggingBean;
@PostConstruct
public void start() {
loggingBean.logInfo("Started!");
myCache = new HashMap<Integer, String>();
}
@Lock(LockType.WRITE)
public void addUser(Integer id, String name) {
myCache.put(id, name);
}
@Lock(LockType.READ)
public String getName(Integer id) {
return myCache.get(id);
}
}
三、单例模式的使用场景
一般来说,大量使用单例可能是一个滥用的信号,你应该在合理情况下使用单例:
- 跨越整个应用程序域来访问共享数据,比如配置数据
- 只加载并缓存代价高傲的资源一次,这样可以做到全局共享访问并改进性能
- 创建应用日志实例,因为通常情况下只需要一个即可
- 管理实现了工厂模式的类中的对象
- 创建门面对象,因为通常情况下只需要一个即可
- 延迟创建静态类,单例可以做到延迟实例化
对于重要的缓存解决方案来说,请考虑使用框架,比如:Ehcache、Java Caching System
参考自:《Java EE设计模式解析与应用》
单例模式——java设计模式的更多相关文章
- java设计模式解析(11) Chain责任链模式
设计模式系列文章 java设计模式解析(1) Observer观察者模式 java设计模式解析(2) Proxy代理模式 java设计模式解析(3) Factory工厂模式 java设计模式解析(4) ...
- java设计模式解析(1) Observer观察者模式
设计模式系列文章 java设计模式解析(1) Observer观察者模式 java设计模式解析(2) Proxy代理模式 java设计模式解析(3) Factory工厂模式 java设计模式解析( ...
- java设计模式- (1)单例模式
参加校园招聘的笔试,发现公司都会考一些java设计模式,所以上网查询相关内容,总结常用的几种单例模式. 单例模式(Singleton Pattern)是 Java中最简单的设计模式之一.这种类型的设计 ...
- Java 设计模式泛谈&装饰者模式和单例模式
设计模式(Design Pattern) 1.是一套被反复使用.多人知晓的,经过分类编目 的 代码设计经验总结.使用设计模式是为了可重用代码,让代码更容易维护以及扩展. 2.简单的讲:所谓模式就是得到 ...
- java 设计模式之单例模式
-------Success is getting what you want, happiness is wanting what you get. java设计模式之单例模式(Singleton) ...
- Java设计模式之《单例模式》及应用场景
摘要: 原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6510196.html 所谓单例,指的就是单实例,有且仅有一个类实例,这个单例不应该 ...
- 单例模式——Java EE设计模式解析与应用
单例模式 目录: 一.何为单例 二.使用Java EE实现单例模式 三.使用场景 一.何为单例 确保一个类只有一个实例,并且提供了实例的一个全局访问点 1.1 单例模式类图 ...
- java设计模式单例模式 ----懒汉式与饿汉式的区别
常用的五种单例模式实现方式 ——主要: 1.饿汉式(线程安全,调用率高,但是,不能延迟加载.) 2.懒汉式(线程安全,调用效率不高,可以延时加载.) ——其他: 1.双重检测锁式(由于JVM底层内部模 ...
- 折腾Java设计模式之单例模式
博文原址:折腾Java设计模式之单例模式 单例模式 Ensure a class has only one instance, and provide a global point of access ...
随机推荐
- python 开发学习
https://www.cnblogs.com/wj-1314/p/8476197.html
- kosaraju算法
这个是求一个图有几个强联通分量的算法 先讲一下应该流程 首先输入一个图G,创建一个反向的图GT 图G 对图进行dfs遍历,纪录每个点结束搜索的时间p[i] p[1]=2 p[2]=1 p[3]=5 ...
- bootstrap2.2相关文档
本节课我们主要学习一下 Bootstrap表单和图片功能,通过内置的 CSS定义,显示各种丰富的效果. 一.表单 Bootstrap提供了一些丰富的表单样式供开发者使用. 1.基本格式 //实现基本的 ...
- nuget.org无法解析的办法
今天想学习ef框架,就着手安装最新的ef啦.可是遇到了问题,提示 未能解析此远程名称:'nuget.org' 就去上网找资料啦,发现原来是被墙了,表示无奈. 网上的资料提示,修改hosts文件或是dn ...
- .net core 分布式配置中心
github地址 https://github.com/wangchengqun/ratel 配置文件 数据同步端口 Server: ip: 127.0.0.1 port: 7890 浏览器访问 ht ...
- 【转】TOP10美国虚拟主机/网站空间推荐
原文:http://www.laozuo.org 不同的站长用户需要不同的主机产品,并不是所有的站长, 所有的网站都想放置在VPS服务器中的.虚拟主机也有虚拟主机的方便和优势,下面为老左精选的10个比 ...
- linux 服务器脚本采集数据中文无法执行错误
问题描述:在 RHEL6 版本的服务器上使用脚本操作数据库,其中一个SQL的字段值为中文,每次使用 crontab定时 执行该脚本无法获取数据,实现预期效果,而手动执行正常. oracle clien ...
- vue项目经验:图形验证码接口get请求处理
一般图形验证码处理: 直接把img标签的src指向这个接口,然后在img上绑定点击事件,点击的时候更改src的地址(在原来的接口地址后面加上随机数即可,避免缓存) <img :src=" ...
- tomcat设置默认欢迎页、server.xml配置文件中的标签理解
一:要求:输入网址,不加文件名便可以访问默认页面 (1)项目中只有静态文件 方法:更改tomcat下的conf目录下的web.xml文件,如下图: <welcom-file-list>元素 ...
- 机器学习-Matplotlib绘图(柱状图,曲线图,点图)
matplotlib 作为机器学习三大剑客之一 ,比热按时无比强大的 matplotlib是绘图库,所以呢我就分享一下简单的绘图方式 #柱状图 #导报 柱状图 import matplotlib. ...