1.懒汉模式
懒汉模式:私有构造方法,在静态的实例方法中进行判断是否已经实例化过一次,如果已经被实例化则直接返回对象,没有则进行实例
当前代码属于线程不安全的形式:
在高并发多线程的情况中,可能会有多个线程同时进入了if判断的语句,然后会进行多个实例的创建;若构造方法中有一些复杂的逻辑操作或数据的初始化就会造成严重的影响。
/**
* 懒汉模式:
* 单例的实例在第一次使用时进行创建
*/
public class SingletonExample1 {
//私有的构造函数
private SingletonExample1(){
}
//单例的对象
public static SingletonExample1 instance = null;
//静态的工厂方法
public static SingletonExample1 getInstance(){
if (instance==null){
instance = new SingletonExample1();
}
return instance;
}
}
线程安全的形式:
- 只需要在对应的静态工厂方法前加上synchronized关键字,虽然保证了线程的安全,但是带来了性能上的开销,并不推荐
- 对代码块的加锁方式,同时对instance使用volatile关键字+双重检测的方式进行限制指令重排的发生
如果不使用volatile,直接代码块的加锁进行双重检测,会发生指令重排。
初始化实例的操作:
1.memory = allocate() 分配对象内存空间
2.ctorInstance()初始化对象
3.instance = memory 设置instance指向刚分配的内存
当发生指令重排后:初始化的顺序会编程1,3,2
简单举例:当A线程已经走到new SingletonExample1时,发生指令重排只进行到了3还没有进行步骤2的初始化对象;而线程B走到了外层的if判断语句就会认为已经创建完成实例,此时会直接返回instance,最后导致B线程获得的是一个没有初始化对象的实例。
解决线程安全的代码如下:
//单例的对象,volatile限制发生指令重排,解决了双重检测机制的指令重排引起的线程不安全的问题
public static volatile SingletonExample1 instance = null;
//静态的工厂方法
public static SingletonExample1 getInstance(){
if (instance==null){//双重检测机制
synchronized (SingletonExample1.class){//同步锁
if (instance==null){
instance = new SingletonExample1();
}
}
}
return instance;
}
2.饿汉模式
饿汉模式:在类加载时就进行创建,属于线程安全的形式
饿汉模式的不足:
- 如果构造方法中有过多的处理会导致加载时特别的慢
- 若只进行了类的加载但没有实际的调用,会造成资源的浪费
/**
* 饿汉模式:
* 单例的实例在类装载时进行创建
*/
public class SingletonExample2 {
//私有的构造函数
private SingletonExample2(){
}
//单例的对象
public static SingletonExample2 instance = new SingletonExample2();
//静态的工厂方法
public static SingletonExample2 getInstance(){
return instance;
}
}
单例最安全的方式:
/**
* 枚举式:最安全的
*/
public class SingletonExample7 {
//私有的构造函数
private SingletonExample7(){
}
public static SingletonExample7 getInstance(){
return Singleton.INSTANCE.getInstance();
}
private enum Singleton{
INSTANCE;
private SingletonExample7 singleton;
//JVM保证这个方法绝对只被调用一次
Singleton(){
singleton = new SingletonExample7();
}
public SingletonExample7 getInstance(){
return singleton;
}
}
}