单例模式

一、饿汉模式(在类加载时单例初始化完成,保证getInstance的时候,单例是已经存在)

public class Singleton{
    private static final Singleton SINGLETON=new Singleton();
    private  Singleton(){};
    public static Singleton getInstance(){
        return SINGLETON;
    }
}

二、懒汉模式(只有当调用getInstance的时候,才初始化单例)

public class  Singleton1{
    private  static Singleton1 singleton1=null;
    private  Singleton1(){};
    public static Singleton1 getInstance(){
        if(singleton1==null){
            singleton1=new Singleton1();
        }
        return singleton1;
    }
}

当在多线程的情况下,可能会由线程异步导致创建多个实例的情况,改进的办法

方法一:给方法加锁

class  Singleton2{
    private  static Singleton2 singleton2=null;
    private  Singleton2(){};
    public static synchronized Singleton2 getInstance(){
        if(singleton2==null){
            singleton2=new Singleton2();
        }
        return singleton2;
    }
}

方法二:双重检验

其实,当Singleton的变量为空的时候,我们才进行实例化,所以我们可以把方法一优化为锁的方法块,减少同步时间

同时我们还要避免无序写入

为解释该问题,考察下面代码块清单中的 //3 行。此行代码创建了一个 Singleton 对象并初始化变量 instance 来引用此对象。这行代码的问题是:在 Singleton 构造函数体执行之前,变量 instance 可能成为非 null 的,即赋值语句在对象实例化之前调用,此时别的线程得到的是一个还会初始化的对象,这样会导致系统崩溃。
假设代码执行以下事件序列:

1、线程 1 进入 getInstance() 方法。
2、由于 instance 为 null,线程 1 在 //1 处进入 synchronized 块。 
3、线程 1 前进到 //3 处,但在构造函数执行之前,使实例成为非 null。 
4、线程 1 被线程 2 预占。
5、线程 2 检查实例是否为 null。因为实例不为 null,线程 2 将 instance 引用返回给一个构造完整但部分初始化了的 Singleton 对象。 
6、线程 2 被线程 1 预占。
7、线程 1 通过运行 Singleton 对象的构造函数并将引用返回给它,来完成对该对象的初始化。

为展示此事件的发生情况,假设代码行 instance =new Singleton(); 执行了下列伪代码:
mem = allocate();             //为单例对象分配内存空间.
instance = mem;               //注意,instance 引用现在是非空,但还未初始化
ctorSingleton(instance);    //为单例对象通过instance调用构造函数

//方法二:双重检验,因为每次在判断为空后才进行初始化,所以给new对象加锁就行
class  Singleton3{
    private  static volatile Singleton3 singleton3=null;
    private  Singleton3(){};
    public static  Singleton3 getInstance(){
        if(singleton3==null){
            synchronized(Singleton3.class){//1
                if(singleton3==null){//2               
                singleton3=new Singleton3();//3
                }
            }
        }
        return singleton3;
    }
}

方法三:静态内部类

既实现了线程安全,又避免了同步带来的性能影响的方法

//静态内部类,避免加锁浪费时间
class Singleton4{
     private static class LazySingleton{
     private  static final Singleton4 SINGLETON_4=new Singleton4();
    }
    private Singleton4(){ }
    public static  final Singleton4 getInstance(){
        return LazySingleton.SINGLETON_4;
    }
}

 三、比较两种设计模式的优缺点

饿汉模式:

在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,

懒汉模式:

延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值