/**
*/
private static Singleton singleton;
/**
*/
private Singleton() {
}
/**
*/
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
public void doSomething() {
}
public class Client {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
instance.doSomething();
}
}
单例模式中的线程安全问题
在高并发多线程访问单例的情况下,上述代码是不安全的。为什么?假设第一个线程执行到getInstance()中的singleton = new Singleton()时,第二个线程也开始访问getInstance()方法,但是此时第一个线程要获取的实例对象还没完成,也就是说new Singleton()需要一定的时间,那么此时第二个线程访问判断singleton == null的条件还是成立的,那就会进行创建对象的过程。因此这时会出现创建了两个对象,两个线程访问的不是同一个对象,假如有若干线程同时访问,则产生对象的数量不唯一。
解决单例线程安全的方法:
1. 将访问方法变为同步方法(synchronized)
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
}
}
使用synchronized关键字可以避免多线程访问的安全问题,这是java语言的特性,synchronized使得一个线程在访问此方法时必须等待另一个线程离开该方法,相当于给方法加锁,但是这样也不是最佳的方式,因为一旦加了synchronized关键字就意味着在每次访问时都要进行同步操作,而同步是影响性能的,假如同步造成的性能影响对系统来说可以忽略,那么你可以不必在意。
2.使用饿汉单例模式
上述代码所示例的单例模式又称为懒汉单例模式,与之对应的还有一种就是饿汉单例模式,懒汉是将对象初始化延时处理,而饿汉则是在定义成员变量的时候直接初始化对象:
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return singleton;
}
}
这种方式是直接静态初始化一个实例对象,Java虚拟机JVM在加载类的时候就会创建该实例,这样就能保证在任何线程访问的时候该实例一定存在且唯一了。因为单例模式的目的就是确保存在一个唯一对象, 如果直接静态初始化带来的负担不是很大,可以考虑这种方式。但是如果new Singleton()需要的代价比较大,比如会占用极高的内存或者比较耗时,那么你需要慎重考虑了,因为假如实例对象并没有机会被访问到(假设你只用到了其中的静态方法),那么直接静态初始化了一个对象是不是浪费了资源呢。
饿汉和懒汉相比,还有一种极端的情况就是,假如对象长期未被使用,JVM是有可能回收掉该对象的,一旦被回收掉,懒汉模式下getInstance()会重新创建该对象,而饿汉模式下getInstance()则会直接返回null,但是这种情况现在基本不会存在,在java1.2以前会存在这个问题,后续的jdk版本中java修复了这个bug。
3.使用双重检查加锁减少同步次数(Double Check Lock)
为了减少同步的次数,我们可以将synchronized移到方法的内部,因为实际上一旦我们创建好了实例对象,实例对象已经存在的情况下,其它线程再来访问肯定是同一个对象。所以只需要在第一次访问的时候提供同步加锁就可以,第二次对象已存在就不需要再加锁了。
public class Singleton {
/*使用volatile保证对该变量的写入对另一个线程可见/
private volatile static Singleton mSingleton = null;
private Singleton(){}
public static Singleton getInstance() {
if (mSingleton == null) {
synchronized (Singleton.class) {
if (mSingleton == null) {
mSingleton = new Singleton();
te Singleton(){}
public static Singleton getInstance() {
if (mSingleton == null) {
synchronized (Singleton.class) {
if (mSingleton == null) {
mSingleton = new Singleton();