单例模式里有懒汉式和饿汉式,在多线程下需要用特殊的方式保证线程安全。
下面我用一种方式实现饿汉式单例,两种方式实现懒汉式单例
多线程下饿汉式单例:
//使用final修饰:防止子类覆盖父类中的方法,破坏单例
public final class singleton implements Serializable {
//构造方法私有化:防止其他类创建实例
private singleton(){}
//创建实例,将其设置为static和final
private static final singleton INSTANCE=new singleton();
//提供静态方法获取创建的实例
public static singleton getInstance(){
return INSTANCE;
}
//反序列化时,返回自己创建的实例而不是字节码生成的实例
public Object readResolve(){
return INSTANCE;
}
}
多线程下懒汉式单例:DCL方式
public class singleton {
//私有化构造方法
private singleton(){}
//先定义实例化对象为null,并且用volatitle修饰
//加volatile的修饰的原因:因为同步代码块中的指令可能发生指令重排序,因为同步代码块内的构造方法和其赋值的指令,
//如果不加volatitle,这两个字节码指令可能会重排序,即先执行完赋值,再去调用构造方法
// 若此时有第二个线程来获取实例,则会产生错误(拿到的是没有调用构造前的值)
private static volatile singleton INSTANCE=null;
//创建实例的方法
public static singleton getInstance(){
if(INSTANCE!=null){
return INSTANCE;//第一个线程创建后实例后,其他线程就不用创建实例了
}
synchronized (singleton.class){
//在此处再次判断的原因是:假如有两个线程t1,t2,假设t1第一次执行,它先进行第一个判断,不成立,去获取锁,成功,进入同步
//代码块,去创建实例,还没有创建完成,这时t2线程执行到此,去获取锁,因为t1线程还持有锁,t2线程被阻塞住了,等t1线程
//创建实例完成,赋值完成,释放锁,退出同步代码块,t2线程获取到锁,进入同步代码块,此时如果不进行判断,t2线程也会去
//创建实例,这就不是单实例了
if(INSTANCE!=null){
return INSTANCE;
}
INSTANCE=new singleton();
return INSTANCE;
}
}
}
去掉注释:
public class singleton {
private singleton(){}
private static volatile singleton INSTANCE=null;
public static singleton getInstance(){
if(INSTANCE!=null){
return INSTANCE;
}
synchronized (singleton.class){
if(INSTANCE!=null){
return INSTANCE;
}
INSTANCE=new singleton();
return INSTANCE;
}
}
}
多线程下懒汉式单例:使用静态内部类
public class singleton {
//私有化构造方法
private singleton(){}
//定义静态内部类,创建实例
private static class LazyHolder{
static final singleton INSTANCE=new singleton();
}
//提供获取实例的方法
public static singleton getInstance(){
return LazyHolder.INSTANCE;
}
}
//如何保证其单例的?
//因为静态内部类对外不可见,其加载方式也是懒汉式的,只有用到其实例时才创建
//比如,若是只使用singleton,而没有用getInstance(),不会触发静态内部类的加载,因而也不会去创建实例
//又由于类加载时创建的实例是JVM完成的,可以由JVM保证其线程安全性