相信单例模式大家已经不陌生, 是比较常见的几种模式之一,在spring中默认的bean都是单例.
下面对该模式做下回顾与总结.
主要分为饿汉式和懒汉式, 饿汉式是在类加载时进行初始化, 懒汉式是延迟加载, 当第一次调用时,进行初始化.
单例模式需要注意的是:
1. 要有私有构造方法, 避免外部直接实例化.
2. 要有静态的getInstance()方法.
3. 对于懒汉式, 要在getInstance()方法前加上同步, 避免多线程环境下出现并发问题.
单例模式基本传统实现:
//饿汉式 public class Singleton { //将构造函数私有化 private Singleton(){ } private static Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; } } //懒汉式 public class Singleton1 { private static Singleton1 instance = null; //将构造函数私有化 private Singleton(){ } public static synchronized Singleton1 getInstance() { if(instance == null){ instance = new Singleton1(); } return instance; } }
这个实现对于懒汉式有个问题, 就是同步了整个方法, 这样锁的粒度过大, 会影响多线程环境下的效率. 这样, 我们可以采用DCL模式进行细粒度的加锁. 如下.
public class Singleton { private static volatile Singleton st; private Singleton (){} public static Singleton getInstance() { if (null == st) { synchronized (Singleton.class) { if (st == null) { st = new Singleton(); return st; } } } return st; } }
注意: 此种DCL模式只有在jdk1.4 之后才有效, 因为java5对内存模型作了重要的改动,其中最主要的改动就是对volatile和final语义的改变.
在java 5中多增加了一条happen-before规则:
- 对volatile字段的写操作happen-before后续的对同一个字段的读操作。
利用这条规则我们可以将instance声明为volatile.
![](/qrcode.jpg)
关于happen-before
我们一般说一个操作happen-before另一个操作,这到底是什么意思呢?当说操作A happen-before操作B时,我们其实是在说在发生操作B之前,操作A对内存施加的影响能够被观测到。所谓“对内存施加的影响”就是指对变量的写入,“被观测到”指当读取这个变量时能够得到刚才写入的值(如果中间没有发生其它的写入)。
下面是我列出的三条非常重要的happen-before规则,利用它们可以确定两个操作之间是否存在happen-before关系。
- 同一个线程中,书写在前面的操作happen-before书写在后面的操作。这条规则是说,在单线程 中操作间happen-before关系完全是由源代码的顺序决定的,这里的前提“在同一个线程中”是很重要的,这条规则也称为单线程规则 。这个规则多少说得有些简单了,考虑到控制结构和循环结构,书写在后面的操作可能happen-before书写在前面的操作,不过我想读者应该明白我的意思。
- 对锁的unlock操作happen-before后续的对同一个锁的lock操作。这里的“后续”指的是时间上的先后关系,unlock操作发生在退出同步块之后,lock操作发生在进入同步块之前。这是条最关键性的规则,线程安全性主要依赖于这条规则。但是仅仅是这条规则仍然不起任何作用,它必须和下面这条规则联合起来使用才显得意义重大。这里关键条件是必须对“同一个锁”的lock和unlock。
- 如果操作A happen-before操作B,操作B happen-before操作C,那么操作A happen-before操作C。这条规则也称为传递规则。
还有一种利用延迟加载实现懒汉式单例模式的例子:
public class Singleton4 { private static class SingletonHolder { public static final Singleton4 INSTANCE = new Singleton4(); } private Singleton4() {} public static Singleton4 getInstance() { return SingletonHolder.INSTANCE; } }
推荐一篇好文: