转:http://hukai.me/java-notes-singleton-pattern/

这里不赘述单例模式的概念了,直接演示几种不同的实现方式。

0)Eager initialization

如果程序一开始就需要某个单例,并且创建这个单例并不那么费时,我们可以考虑用这种方式:

1
2
3
4
5
6
7
8
9
public class Singleton {
private static final Singleton INSTANCE = new Singleton(); private Singleton() {} public static Singleton getInstance() {
return INSTANCE;
}
}

这种实现方式有几个特点:

  • 实例一开始就被创建(Eager initialization)。
  • getInstance()方法不需要加synchronize来解决多线程同步的问题。
  • final关键字确保了实例不可变,并且只会存在一个。

1)Lazy initialization

懒加载的方式使得单例会在第一次使用到时才会被创建.先看一种有隐患的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public final class LazySingleton {
private static volatile LazySingleton instance = null; // private constructor
private LazySingleton() {
} public static LazySingleton getInstance() {
if (instance == null) {
synchronized (LazySingleton.class) {
instance = new LazySingleton();
}
}
return instance;
}
}

请注意:上面的写法其实非线程安全的,假设两个Thread同时进入了getInstance方法,都判断到instance==null,然后会因为synchronized的原因,逐个执行,这样就得到了2个实例。解决这个问题,需要用到典型的double-check方式,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class LazySingleton {
private static volatile LazySingleton instance = null; private LazySingleton() {
} public static LazySingleton getInstance() {
if (instance == null) {
synchronized (LazySingleton .class) {
if (instance == null) {
instance = new LazySingleton ();
}
}
}
return instance;
}
}

另外一个更简略直观的替代写法是:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class LazySingleton {
private static volatile LazySingleton instance = null; private LazySingleton() {
} public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton ();
}
return instance;
}
}

2)Static block initialization

如果我们对程序的加载顺序有点了解的话,会知道Static block的初始化是执行在加载类之后,Constructor被执行之前。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class StaticBlockSingleton {
private static final StaticBlockSingleton INSTANCE; static {
try {
INSTANCE = new StaticBlockSingleton();
} catch (Exception e) {
throw new RuntimeException("Error, You Know This, Haha!", e);
}
} public static StaticBlockSingleton getInstance() {
return INSTANCE;
} private StaticBlockSingleton() {
// ...
}
}

上面的写法有一个弊端,如果我们类有若干个static的变量,程序的初始化却只需要其中的1,2个的话,我们会做多余的static initialization。

3)Bill Pugh solution

University of Maryland Computer Science researcher Bill Pugh有写过一篇文章initialization on demand holder idiom

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton {
// Private constructor prevents instantiation from other classes
private Singleton() { } /**
* SingletonHolder is loaded on the first execution of Singleton.getInstance()
* or the first access to SingletonHolder.INSTANCE, not before.
*/
private static class SingletonHolder {
public static final Singleton INSTANCE = new Singleton();
} public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

SingletonHolder类会在你需要的时候才会被初始化,而且它不影响Singleton类的其他static成员变量的使用。这个方法是线程安全的并且避免了使用volatile与synchronized。

4)Using Enum

这是最简便安全的方法。没有明显的缺点,并且避免了下面要讲到的序列化的隐患。

1
2
3
4
5
6
public enum Singleton {
INSTANCE;
public void execute (String arg) {
// perform operation here
}
}

Serialize and de-serialize

在某些情况下,需要实现序列化的时候,普通的单例模式需要添加readResolve的方法,不然会出现异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class DemoSingleton implements Serializable {
private volatile static DemoSingleton instance = null; public static DemoSingleton getInstance() {
if (instance == null) {
instance = new DemoSingleton();
}
return instance;
} protected Object readResolve() {
return instance;
} private int i = 10; public int getI() {
return i;
} public void setI(int i) {
this.i = i;
}
}

仅仅有上面的还不够,我们需要添加serialVersionUID,例子详见下面的总结。

Conclusion

实现一个功能完善,性能更佳,不存在序列化等问题的单例,建议使用下面两个方式之一:

Bill Pugh(Inner Holder)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class DemoSingleton implements Serializable {
private static final long serialVersionUID = 1L; private DemoSingleton() {
// private constructor
} private static class DemoSingletonHolder {
public static final DemoSingleton INSTANCE = new DemoSingleton();
} public static DemoSingleton getInstance() {
return DemoSingletonHolder.INSTANCE;
} protected Object readResolve() {
return getInstance();
}
}

Enum

1
2
3
4
5
6
public enum Singleton {
INSTANCE;
public void execute (String arg) {
// perform operation here
}
}

参考资料

Java Notes 00 - Singleton Pattern(单例总结)的更多相关文章

  1. 设计模式(三)Singleton Pattern单例设计模式

    1.饿汉式 public class SingletonDemo { private static SingletonDemo s=new SingletonDemo(); private Singl ...

  2. 【工作中学习1】两个设计模式:Singleton(单例)和 Adapter(适配器)

    好久没有写自己的学习小博客,罪过罪过..最近本菜鸟在项目中接触到经常用到的设计模式,首先是Singleton(单例),这个相信大家都会用到很多,所以自己用代码实现一下,有助于自己学习理解,如有不对,请 ...

  3. Java设计模式透析之 —— 单例(Singleton)

    写软件的时候经常需要用到打印日志功能,可以帮助你调试和定位问题,项目上线后还可以帮助你分析数据.但是Java原生带有的System.out.println()方法却很少在真正的项目开发中使用,甚至像f ...

  4. 《JAVA笔记 day08 静态_单例》

    //static关键字: /* 静态方法的使用注意事项: 1,静态方法不能访问非静态的成员. 但是非静态是可以访问静态成员的. 说明:静态的弊端在于访问出现了局限性.好处是可以直接别类名调用. 2,静 ...

  5. Singleton(单例)模式和Double-Checked Locking(双重检查锁定)模式

    问题描述 现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能:在实际开发过程中,会专门有一个日志模块,负责写日志,由于在系统的任何地方,我们都有可能要调用日志模块中的函数,进 ...

  6. The Java Enum: A Singleton Pattern [reproduced]

    The singleton pattern restricts the instantiation of a class to one object. In Java, to enforce this ...

  7. Spring IoC 中的(Singleton)单例对象创建过程探索

    前言 之前将spring framework 源码导入了idea,后来折腾调试了一下,于是研究了一下最简单的singleton对象在spring中是如何创建的.这里所谓的简单,就是指无属性注入,无复杂 ...

  8. 用私有构造器或者枚举类型强化SingleTon(单例)属性

    单例(singleton)就是一个只实例化一次的类.使类成为单例可能会使它的测试变得困难,因为除非它实现了作为其类型的接口,否则不可能用模拟实现来代替这个单例.下面是几种实现单例的方法: 1.共有静态 ...

  9. java设计模式—单例模式(包含单例的破坏)

    什么是单例模式? 保证一个了类仅有一个实例,并提供一个访问它的全局访问点. 单例模式的应用场景? 网站的计数器,一般也是采用单例模式实现,否则难以同步: Web应用的配置对象的读取,一般也应用单例模式 ...

随机推荐

  1. typedef和define的作用域

    typedef: 如果放在所有函数之外,它的作用域就是从它定义开始直到文件尾: 如果放在某个函数内,定义域就是从定义开始直到该函数结尾: #define: 不管是在某个函数内,还是在所有函数之外,作用 ...

  2. 写sql语句注意事项

    做管理系统的,无论是bs结构的还是cs结构的,都不可避免的涉及到数据库表结构的设计,sql语句的编写等.因此在开发系统的时候,表结构设计是否合理,sql语句是否标准,写出的sql性能是否优化往往会成为 ...

  3. HDU 2653 - Waiting ten thousand years for Love

    首先,对于一个 '@' 飞上去,飞下来都要耗1点魔力,所以是两点= = 然后站在同一格 魔力可能不同,所以要增加一维. 还有当前搜到的不一定是最小. 别的也没啥. #include <iostr ...

  4. Unique Binary Search Trees,Unique Binary Search Trees II

    Unique Binary Search Trees Total Accepted: 69271 Total Submissions: 191174 Difficulty: Medium Given  ...

  5. opals 开发记录

    1:开发需要的文件 下载地址:http://www.geo.tuwien.ac.at/opals/html/index.html 注意只能在release下才能通过. 我自己整理好的(64位的),以备 ...

  6. oracle使用口令文件验证和os验证

    一.Oracle安装之后默认情况下是启用了OS认证的,这里提到的os认证是指服务器端os认证.OS认证的意思把登录数据库的用户和口令校验放在了操作系统一级.如果以安装Oracle时的用户登录OS,那么 ...

  7. PHP实现简单爬虫

    <?php /**  * 爬虫程序 -- 原型  *  * 从给定的url获取html内容  *  * @param string $url  * @return string  */ func ...

  8. 【测试技术】ant中的for循环用法

    有的时候,我们希望ant中也能类似脚本语言一样进行for循环,以实现一些重复性工作.由于ant核心包并未提供此功能,所以需要下载一个扩展包扔到ant的lib目录下去.详细步骤如下: 1.下载核心包:a ...

  9. 编译安装mysql5.7.9

    第一步:安装一些可能会用到的依赖 yum -y install gcc-c++ ncurses-devel cmake make perl gcc autoconf automake zlib lib ...

  10. Azure上Linux VM DDOS攻击预防: 慢速攻击

    在上篇博客(http://www.cnblogs.com/cloudapps/p/4996046.html)中,介绍了如何使用Apache的模块mod_evasive进行反DDOS攻击的设置,在这种模 ...