Java设计模式の单例模式
--------------------------------------------------
目录
--------------------------------------------------
1.定义
确保一个类只有一个实例,并提供一个全局访问点!
2.常见的集中单例实现
a.饿汉式,线程安全 但效率比较低
/**
* 单例模式的实现:饿汉式,线程安全 但效率比较低
*/
public class SingletonTest { // 定义一个私有的构造方法
private SingletonTest() {
} // 将自身的实例对象设置为一个属性,并加上Static和final修饰符
private static final SingletonTest instance = new SingletonTest(); // 静态方法返回该类的实例
public static SingletonTest getInstancei() {
return instance;
} }
b.单例模式的实现:饱汉式,非线程安全
/**
* 单例模式的实现:饱汉式,非线程安全
*
*/
public class SingletonTest { // 定义私有构造方法(防止通过 new SingletonTest()去实例化)
private SingletonTest() {
} // 定义一个SingletonTest类型的变量(不初始化,注意这里没有使用final关键字)
private static SingletonTest instance; // 定义一个静态的方法(调用时再初始化SingletonTest,但是多线程访问时,可能造成重复初始化问题)
public static SingletonTest getInstance() {
if (instance == null)
instance = new SingletonTest();
return instance;
}
}
c.饱汉式,线程安全简单实现
/**
* 单例模式的实现:饱汉式,线程安全简单实现
*
*/
public class SingletonTest { // 定义私有构造方法(防止通过 new SingletonTest()去实例化)
private SingletonTest() {
} // 定义一个SingletonTest类型的变量(不初始化,注意这里没有使用final关键字)
private static SingletonTest instance; // 定义一个静态的方法(调用时再初始化SingletonTest,使用synchronized 避免多线程访问时,可能造成重的复初始化问题)
public static synchronized SingletonTest getInstance() {
if (instance == null)
instance = new SingletonTest();
return instance;
}
}
d.线程安全 并且效率高 单例模式最优方案
/**
* 单例模式最优方案
* 线程安全 并且效率高
*
*/
public class SingletonTest { // 定义一个私有构造方法
private SingletonTest() { }
//定义一个静态私有变量(不初始化,不使用final关键字,使用volatile保证了多线程访问时instance变量的可见性,避免了instance初始化时其他变量属性还没赋值完时,被另外线程调用)
private static volatile SingletonTest instance; //定义一个共有的静态方法,返回该类型实例
public static SingletonTest getIstance() {
// 对象实例化时与否判断(不使用同步代码块,instance不等于null时,直接返回对象,提高运行效率)
if (instance == null) {
//同步代码块(对象未初始化时,使用同步代码块,保证多线程访问时对象在第一次创建后,不再重复被创建)
synchronized (SingletonTest.class) {
//未初始化,则初始instance变量
if (instance == null) {
instance = new SingletonTest();
}
}
}
return instance;
}
}
e:静态内部类方式
/**
* 静态内部类方式
*
*/
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
10
这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别):第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。
3.总结
【以上单例模式】传统的两私有一公开(私有构造方法、私有静态实例(懒实例化/直接实例化)、公开的静态获取方法)涉及线程安全问题(即使有多重检查锁也可以通过反射破坏单例)
目前最为安全的实现单例的方法是通过内部静态enum的方法来实现,因为JVM会保证enum不能被反射并且构造器方法只执行一次。如下
a.使用枚举的单例模式
/**
* 使用枚举的单例模式
*
* @author yzl
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class EnumSingleton{
private EnumSingleton(){}
public static EnumSingleton getInstance(){
return Singleton.INSTANCE.getInstance();
} private static enum Singleton{
INSTANCE; private EnumSingleton singleton;
//JVM会保证此方法绝对只调用一次
private Singleton(){
singleton = new EnumSingleton();
}
public EnumSingleton getInstance(){
return singleton;
}
}
}
b.使用枚举,static处调用,初始化一次
import java.util.ArrayList;
import java.util.List; /**
* 初始化的优雅实现
* 可以在static处调用,
* 也可以在普通方法里调用,都保证只初始化一次
*
* 当然将enum块的代码直接放到StaticInitTest类的private static 方法里做也是可以的
*
* @author yzl
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class StaticInitTest {
private static List<Integer> dataList = null; static{
dataList = Singleton.INSTANCE.init();
} /**
*
* 单例模式来填充数据
*
* @author yzl
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
private static enum Singleton {
INSTANCE;
private List<Integer> list; private Singleton(){
fillData();
}
/**
*
* 初始化数据
*
* @see [相关类/方法](可选)
* @since [产品/模块版本](可选)
*/
private void fillData(){
list = new ArrayList<Integer>(5);
for(int i =1; i<6; i++){
list.add(i);
}
} /**
*
* 初始化的入口
*
* @see [相关类/方法](可选)
* @since [产品/模块版本](可选)
*/
public List<Integer> init(){
return list;
}
}
}
最终总结
有两个问题需要注意:
1、如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类 装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。
2、如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。
对第一个问题修复的办法是:
private static Class getClass(String classname) throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if(classLoader == null)
classLoader = Singleton.class.getClassLoader(); return (classLoader.loadClass(classname));
}
}
对第二个问题修复的办法是:
public class Singleton implements java.io.Serializable {
public static Singleton INSTANCE = new Singleton(); protected Singleton() { }
private Object readResolve() {
return INSTANCE;
}
}
对我来说,我比较喜欢第a和e种方式,简单易懂,而且在JVM层实现了线程安全(如果不是多个类加载器环境),一般的情况下,我会使用第a种方式,只有在要明确实现lazy loading效果时才会使用第e种方式,另外,如果涉及到反序列化创建对象时我会试着使用枚举的方式来实现单例,不过,我一直会保证我的程序是线程安全的,如果有其他特殊的需求,我可能会使用第七种方式,毕竟,JDK1.5已经没有双重检查锁定的问题了。
参考资料:java单例之enum实现方式
http://www.blogjava.net/kenzhh/archive/2013/03/15/357824.html
Java设计模式の单例模式的更多相关文章
- java设计模式单例模式 ----懒汉式与饿汉式的区别
常用的五种单例模式实现方式 ——主要: 1.饿汉式(线程安全,调用率高,但是,不能延迟加载.) 2.懒汉式(线程安全,调用效率不高,可以延时加载.) ——其他: 1.双重检测锁式(由于JVM底层内部模 ...
- JAVA设计模式-单例模式(Singleton)线程安全与效率
一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...
- Java设计模式 - - 单例模式 装饰者模式
Java设计模式 单例模式 装饰者模式 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 静态代理模式:https://www.cnblogs.com/StanleyBlogs/p/1 ...
- 【设计模式】Java设计模式 - 单例模式
[设计模式]Java设计模式 - 单例模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 分享学习心得,欢迎指正,大家一起学习成长! 原创作品,更多关注我CSDN: ...
- Java 设计模式 —— 单例模式
1. 概念: 单例模式是一种常用的软件设计模式.核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源.如果 ...
- Java设计模式 - 单例模式 (懒汉方式和饿汉方式)
概念: Java中单例模式是一种常见的设计模式,单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 单例模式的写法有好几种,这 ...
- java设计模式——单例模式(一)
一. 定义与类型 定义:保证一个类仅有一个实例,并提供一个全局访问点 类型:创建型 二. 适用场景 想确保任何情况下都绝对只用一个实例 三. 优缺点 优点: 在内存里只有一个实例,减少了内存开销 可以 ...
- JAVA设计模式--单例模式
单例设计模式 Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点. 核心知识点如下: (1) 将采用单例 ...
- Java设计模式-单例模式(Singleton)
单例对象(Singleton)是一种常用的设计模式.在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在.这样的模式有几个好处: 1.某些类创建比较频繁,对于一些大型的对象,这是一笔 ...
随机推荐
- Java中I/O流之缓冲流
Java 中的缓冲流: 1. 缓冲流要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,提高了读写的效率,同时增加了一些新的方法(带缓冲区的,显著减少对 IO 的读写次数,保护硬盘). 2. ...
- Java之comparable接口
comparable 接口: 1. 问题:java.util.Collections 类中的方法 Collections.sort(List list) 是根据什么确定容器中对象的“大小”顺序的? 2 ...
- python爬虫从入门到放弃(四)之 Requests库的基本使用(转)
什么是Requests Requests是用python语言基于urllib编写的,采用的是Apache2 Licensed开源协议的HTTP库如果你看过上篇文章关于urllib库的使用,你会发现,其 ...
- 制作QQ微信支付宝三合一收款码
转载:http://blog.mambaxin.com/article/56 发现很多博客都带了打赏功能,虽说打赏的人可能很少,但始终是一份心意,能让博主知道自己写的文章有用,能够帮助到人.所以,我也 ...
- C#中的反射和扩展方法的运用
前段时间做了一个练手的小项目,采用的是三层架构,也就是Models,IDAL,DAL,BLL 和 Web , 在DAL层中各个类中有一个方法比较常用,那就是 RowToClass ,顾名思义,也就是将 ...
- [剑指Offer] 58.对称的二叉树
题目描述 请实现一个函数,用来判断一颗二叉树是不是对称的.注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的. [思路]递归,关键是isSame函数中的最后一句 /* struct Tree ...
- 什么情况下需要检测预装Win8/8.1电脑内置激活密钥(即Win8/8.1 OEM key)?
1.针对预装在品牌机中的Win8操作系统,因使用了某些软件或自己操作不当导致系统激活Key被替换而激活失效时,你需要找到内置在电脑中的OEM key,重新输入OEM key来激活系统. 2.针对预装W ...
- 使用户浏览器添加没有的字体@font-face
@font-face的用法 @font-face { font-family: 'MyWebFont'; src: url('webfont.eot'); /* IE9 Compat Modes */ ...
- 用select模拟一个socket server成型版2
1.字典队列测试 import queue msg_dic={} msg_dic[1]=queue.Queue() msg_dic[1].put('hello') msg_dic[1].put('bo ...
- BZOJ1016:[JSOI2008]最小生成树计数——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=1016 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不 ...