相信单例模式大家已经不陌生, 是比较常见的几种模式之一,在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.
关于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;
}
}
推荐一篇好文: