多线程下的单例模式
1、懒汉模式
// 饿汉模式(线程安全)
public class Singleton {
// 私有化构造器,不允许创建对象
private Singleton(){}
// 加载的时候就产生的实例对象ClassLoader
private static Singleton instance = new Singleton();
// 返回实例
public Singleton getInstance(){
return instance;
}
}
2、饿汉模式
// 懒汉模式
public class SingletonDemo {
// volatile的作用是可以使内存中的数据对象线程可见
// 主内存对线程是不可见的,添加volatile关键字之后,主内存对线程可见
// 使用volatile后直接跳过工作内存复制一份的情况,进行主内存操作判断是否为空
// 如果不加volatile,可能会引起指令重排,导致空指针异常
private volatile static SingletonDemo singleton;
private SingletonDemo(){
System.out.println("创建了单例对象!");
}
// double check
public static SingletonDemo getInstance(){
//必须是一个唯一的对象
if(singleton == null){
synchronized (SingletonDemo.class){
if(singleton == null){
singleton = new SingletonDemo();
}
}
}
return singleton;
}
}
【分析】:不加volatile关键字的影响
修改代码:
private Socket socket;
private static SingletonDemo singleton;
private SingletonDemo(){
socket = new Socket();
singleton = new SingletonDemo();
socket.toString();
}
在初始化的时候,根据JVM优化,可能在编译期代码重排,运行期指令重排,导致socket.toString()在没有被初始化的情况下优先执行,就会导致空指针异常,所以必须加volatile关键字,这样singleton = new SingletonDemo();前面的代码就不会被改变执行顺序不会出现空指针了。
内部类下的单例模式
声明类的时候,成员变量中不声明实例变量,而放到内部静态类中。
public class OutHolder {
private OutHolder(){}
private static class InnerHolder{
private static OutHolder holder = new OutHolder();
}
// 懒加载,调用的时候才会初始化(内部类会延迟加载)
public static OutHolder getInstance(){
return InnerHolder.holder;
}
}
枚举类下的单例模式
public enum EnumDemo{
// INSTANCE是一个常量,在加载的时候只实例化一次,类型为EnumDemo
// 相当于EnumDemo INSTANCE = new EnumDemo();
INSTANCE;
public static EnumDemo getInstance(){
return INSTANCE;
}
}
单例模式:
public class EnumSingleton {
private EnumSingleton(){}
// 延迟加载
private enum EnumHolder{
// EnumHolder类型的常量
INSTANCE;
private EnumSingleton instance;
EnumHolder(){
this.instance = new EnumSingleton();
}
public EnumSingleton getInstance(){
return instance;
}
}
public static EnumSingleton getInstance(){
return EnumHolder.INSTANCE.instance;
}
}