"围观"设计模式(7)--创建型之单例模式(Singleton Pattern)
2016-04-23 15:23
543 查看
单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。----维基百科(WIKIPEDIA)
个人的理解:
单例模式概念比较简单,他的目的就是只允许出现一个该类的实例,经常在JDBC操作类等处被用到,我在项目中应用到的地方就是用于获取Dao层的类的实例,单例模式有多种实现方式,这里我认知到的有饿汉式、懒汉式、枚举、静态内部类。庆幸这次整理过程,因为又拓展了不少的单例模式的知识。
下面看实际的例子:
plain copy
</pre><span style="font-size:18px;"><span style="font-size:12px;"></span></span><pre name="code" class="java">public class Singleton01 {
private static Singleton01 singleton = null;
private Singleton01(){}
public static Singleton01 getInstance(){
if(singleton == null)
singleton = new Singleton01();
return singleton;
}
}
线程不安全,当多个线程同时调用getInstance方法时,都检测到singleton为null,然后就开始创建对象了,这时候就会创建多个实例,而不是一个。
[java] view
plain copy
public class Singleton02 {
private static Singleton02 singleton = null;
private Singleton02(){}
public synchronized static Singleton02 getInstance(){
if(singleton == null)
singleton = new Singleton02();
return singleton;
}
}
这个例子线程安全了,多个线程同时调用的时候也不会创建多个对象实例了,但是不是很高效,需要多个线程依次的去执行这个方法。
[java] view
plain copy
public class Singleton03 {
private static Singleton03 singleton = null;
private Singleton03(){}
public static Singleton03 getInstance(){
if(singleton == null)
synchronized (Singleton03.class) {
if(singleton == null){
singleton = new Singleton03();
}
}
return singleton;
}
}
这种方式被称为Double Check(双重检验),比较经典的设计,为什么要两次呢?先说外面的判断是避免对象已经创建好了之后,就不用再去同步执行创建对象去了,第二个是,防止第一次还没有创建实例的时候,多个线程已经在等待中,这个时候需要进行空的判断。防止后面等待的现场进入之后再次创建实例。
参考了其他人的文章【1】:
singleton = new Singleton()不是原子操作,在JVM中大致做了三件事情:
1. 给instance分配内存。
2. 通过其构造函数初始化成员变量。
3. 将instance指向分配的内存空间。(执行完instance就不是null了)。
但是在JVM编译器中存在指令重新排序的优化,最终的执行顺序是1-2-3或者1-3-2,如果是1-3-2的话,那么在3执行完毕,2执行之前,线程A被线程B抢断的话,instance非空,线程B会直接返回instance,这个时候,直接使用就会报错。
给出的解决方式(使用volatile关键字屏蔽重排序)是:
[java] view
plain copy
public class Singleton03 {
private volatile static Singleton03 singleton = null;
private Singleton03(){}
public static Singleton03 getInstance(){
if(singleton == null)
synchronized (Singleton03.class) {
if(singleton == null){
singleton = new Singleton03();
}
}
return singleton;
}
}
饿汉式
[java] view
plain copy
public class Singleton04 {
private static final Singleton04 singleton = new Singleton04();
private Singleton04(){
}
public static Singleton04 getInstance(){
return singleton;
}
}
这种方式是线程安全的,不过不足的一点是,即使没有调用getInstance方法,也会产生他的实例。之前的懒汉式的方式是懒加载的思想,在需要的时候创建。
静态内部类
[java] view
plain copy
public class Singleton05 {
private static class Singleton{
private static final Singleton05 instance = new Singleton05();
}
private Singleton05(){
}
public Singleton05 getInstance(){
return Singleton.instance;
}
}
采用了静态内部类的方式,私有的静态内部类,外部无法访问,类似于饿汉式的形式,但是在此基础上进行了改进,饿汉式是使用的内部属性,而这个是使用的静态内部类的常量。
1. 自由序列化
2. 线程安全
3. 保证只有一个实例。
[java] view
plain copy
public enum Singleton06 {
INSTANCE;
private Singleton06(){
}
public void print(){
System.out.println("Enum Singleton!");
}
}
调用:
[java] view
plain copy
Singleton06.INSTANCE.print();
参考
【1】 http://wuchong.me/blog/2014/08/28/how-to-correctly-write-singleton-pattern/
个人的理解:
单例模式概念比较简单,他的目的就是只允许出现一个该类的实例,经常在JDBC操作类等处被用到,我在项目中应用到的地方就是用于获取Dao层的类的实例,单例模式有多种实现方式,这里我认知到的有饿汉式、懒汉式、枚举、静态内部类。庆幸这次整理过程,因为又拓展了不少的单例模式的知识。
下面看实际的例子:
懒汉式
[java] viewplain copy
</pre><span style="font-size:18px;"><span style="font-size:12px;"></span></span><pre name="code" class="java">public class Singleton01 {
private static Singleton01 singleton = null;
private Singleton01(){}
public static Singleton01 getInstance(){
if(singleton == null)
singleton = new Singleton01();
return singleton;
}
}
线程不安全,当多个线程同时调用getInstance方法时,都检测到singleton为null,然后就开始创建对象了,这时候就会创建多个实例,而不是一个。
[java] view
plain copy
public class Singleton02 {
private static Singleton02 singleton = null;
private Singleton02(){}
public synchronized static Singleton02 getInstance(){
if(singleton == null)
singleton = new Singleton02();
return singleton;
}
}
这个例子线程安全了,多个线程同时调用的时候也不会创建多个对象实例了,但是不是很高效,需要多个线程依次的去执行这个方法。
[java] view
plain copy
public class Singleton03 {
private static Singleton03 singleton = null;
private Singleton03(){}
public static Singleton03 getInstance(){
if(singleton == null)
synchronized (Singleton03.class) {
if(singleton == null){
singleton = new Singleton03();
}
}
return singleton;
}
}
这种方式被称为Double Check(双重检验),比较经典的设计,为什么要两次呢?先说外面的判断是避免对象已经创建好了之后,就不用再去同步执行创建对象去了,第二个是,防止第一次还没有创建实例的时候,多个线程已经在等待中,这个时候需要进行空的判断。防止后面等待的现场进入之后再次创建实例。
参考了其他人的文章【1】:
singleton = new Singleton()不是原子操作,在JVM中大致做了三件事情:
1. 给instance分配内存。
2. 通过其构造函数初始化成员变量。
3. 将instance指向分配的内存空间。(执行完instance就不是null了)。
但是在JVM编译器中存在指令重新排序的优化,最终的执行顺序是1-2-3或者1-3-2,如果是1-3-2的话,那么在3执行完毕,2执行之前,线程A被线程B抢断的话,instance非空,线程B会直接返回instance,这个时候,直接使用就会报错。
给出的解决方式(使用volatile关键字屏蔽重排序)是:
[java] view
plain copy
public class Singleton03 {
private volatile static Singleton03 singleton = null;
private Singleton03(){}
public static Singleton03 getInstance(){
if(singleton == null)
synchronized (Singleton03.class) {
if(singleton == null){
singleton = new Singleton03();
}
}
return singleton;
}
}
饿汉式
[java] viewplain copy
public class Singleton04 {
private static final Singleton04 singleton = new Singleton04();
private Singleton04(){
}
public static Singleton04 getInstance(){
return singleton;
}
}
这种方式是线程安全的,不过不足的一点是,即使没有调用getInstance方法,也会产生他的实例。之前的懒汉式的方式是懒加载的思想,在需要的时候创建。
静态内部类
[java] viewplain copy
public class Singleton05 {
private static class Singleton{
private static final Singleton05 instance = new Singleton05();
}
private Singleton05(){
}
public Singleton05 getInstance(){
return Singleton.instance;
}
}
采用了静态内部类的方式,私有的静态内部类,外部无法访问,类似于饿汉式的形式,但是在此基础上进行了改进,饿汉式是使用的内部属性,而这个是使用的静态内部类的常量。
枚举
神一样的设计,使用枚举实现单例模式的几个原因:1. 自由序列化
2. 线程安全
3. 保证只有一个实例。
[java] view
plain copy
public enum Singleton06 {
INSTANCE;
private Singleton06(){
}
public void print(){
System.out.println("Enum Singleton!");
}
}
调用:
[java] view
plain copy
Singleton06.INSTANCE.print();
代码下载
下载代码参考
【1】 http://wuchong.me/blog/2014/08/28/how-to-correctly-write-singleton-pattern/
相关文章推荐
- 编写一个函数实现atoi()函数,即把字符串数字转变为数字
- Flex Builder中创建的项目4种
- UIApplication
- JavaScript中的onchange、oninput以及onpropertychange
- maven repository mirror:
- Android自定义圆形进度条,结合AsyncTask下载显示进度
- "围观"设计模式(6)--开闭原则(Open/Closed Principle)
- 数据库连接池的工作原理
- 搜索—Problem_1016-Red and Black
- OpenCv学习笔记--支持向量机SVM线性可分情况下的OpenCv实现的超详细注释(2)
- Django - 文件上传
- MySQL绿色版安装(mysql-5.7.12-win32)
- Angular源码解析参考文章
- 命令行——rm命令(删除)详解
- Linux数据重定向详细分析
- CSS3实现2D变换
- Android性能专项测试之battery-historian
- Win7右键没有新建Word、excel、powerpoint
- 数据结构——串
- java cookie详解