线程安全的单例模式 双重效验锁
1.单例模式:确保一个类只有一个实例,自行实例化并向系统提供这个实例(举例 例如有三个线程 使用静态方法,让所创建出来的对象名来调取每一个线程。)
2.单例模式分类:饿单例模式(类加载时实例化一个对象给自己的引用),懒单例模式(调用取得实例的方法如getInstance时才会实例化对象)(java中饿单例模式性能优于懒单例模式,c++中一般使用懒单例模式)
3.单例模式要素:
私有构造方法
私有静态引用指向自己实例
以自己实例为返回值的公有静态方法
饿单例模式:
public static HungrySingleton getInstance {
private static HungrySingleton sc = new HungrySingleton();
private HungrySingleton(){
}
public static HungrySingleton getInstance(){
return sc;
}
}
在程序执行开始的时候,饿单例模式就会new 出新的对象;这种写法就是所谓的饥饿模式,每个对象在没有使用之前就已经初始化了。这就可能带来潜在的性能问题:如果这个对象很大呢?没有使用这个对象之前,就把它加载到了内存中去是一种巨大的浪费。
懒单例模式
public class Singleton {
private Singleton(){
}
private static Singleton sc;
public static synchronized Singleton getInstance(){ //多线程时注意线程安全
if(sc == null){
sc= new Singleton();
}
return sc;
}
}
在程序执行开始的时候,只有在你需要用的时候才会new新对象 意思为空的时候才会new出新对象
双重效验锁
public static SingleClass getInstance(){
if(sc==null){ //第一次校验
synchronized (SingleClass.class){ //同步代码块
if(sc==null){ //第二次校验
sc=new SingleClass(); //创建实例
}
}
}
return sc;
}
此写法保证了,当多个进程进入第一个判断锁时,会被同步机制隔离,只有一个程序进入新建对象,再其他线程进入时,sc已经不为null,因此不会新建多个对象。这种方法就叫做双重检查锁,但是也有一个问题,就是java是实行无序写入的机制,在某个线程执行//2代码的过程中,sc被赋予了地址,但是singleton对象还没构造完成时,如果有线程访问了代码//1此时判断sc不为空,但是方法返回的是一个不完整对象的引用。此时可能会产生错误!