设计模式——单例模式

首先什么是单例模式?为什么需要单例模式?

答:单例模式顾名思义就是在整个运行时,一个类只有一个实例对象

因为有的类的实例对象创建和销毁对资源来说消耗不大,但是有些类比较庞大和复杂如果频繁的创建和销毁对象,并且这些对象完全是可以复用的,会造成不必要的性能浪费

单例模式有多种写法需要考虑:

  • 是否线程安全
  • 是否是懒加载
  • 能否能被反射破坏(这个不用考虑一般都是会被破坏的)

不会满足以上三点的单例

如下:首先来一个简单的单例

public class Singleton {
    private Singleton(){} //构造器私有
    private static Singleton instance = null; //初始化对象为null
    public static Singleton getInstance(){
        if (instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

上面代码如果其他类要使用singleton对象的话只能通过调用getInstance方法,进入方法后首先判断是否被构造过,如果是就直接使用,如果不是就new一个新的出来再使用。

如果要保证在同一时刻只有一个线程进入把代码改成如下代码:

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

 但是现在这个代码有些问题比如:我们只想要在构建的时候同步线程,上面这个就是每次在获取的时候都要同步线程对性能影响很大。这种写法一般不可取。

上面我们发现线程安全出现在了构建对象的阶段,那么我们只要在编译期构建对象,在运行时调用,就不用考虑线程安全问题。代码如下:

public class Singleton {
    private Singleton(){} //构造器私有
    private static Singleton instance = new Singleton();
    public static Singleton getInstance(){
        return instance;
    }
}

但是以上写法不是懒加载的。

我们现在看第二次写的代码,为何性能影响大,原因就在于在方法上定义了synchronized关键字,所以每次进入该方法的时候就必须首先去获取锁。

所以我们把代码改成如下:

public class Singleton {
    private static Singleton singleton;
    private Singleton(){}
    public static Singleton getSingleton(){
       if (singleton == null){
           synchronized (Singleton.class){
               if (singleton == null){
                   singleton = new Singleton();
               }
           }
       }
       return singleton;
    }
}

以上代码运用了双检锁,双检锁是一种很常见的同步代码手段。

下面使用volatile就会阻止指令重排问题

public class Singleton {
    private volatile static Singleton singleton;
    private Singleton(){}
    public static Singleton getSingleton(){
       if (singleton == null){
           synchronized (Singleton.class){
               if (singleton == null){
                   singleton = new Singleton();
               }
           }
       }
       return singleton;
    }
}

上面这种写法既满足了懒加载,又满足了线程安全问题,效率也比较高。

那还有没有更简洁的写法,如下:内部类的写法

public class Singleton {
   private static class SingletonHolder{
       private static final Singleton INSTANCE = new Singleton();
       
   }
   private Singleton(){}
   public static final Singleton getInstance(){
       return SingletonHolder.INSTANCE;
   }
}

这里,静态内部类在程序启动的过程中不会加载,只有在第一次被调用的时候才会被加载。这里主要是运用了jdk加载特性实现了懒加载。

但是上面我们说了会被反射破坏,但是可以利用枚举类型来不让反射破坏,但是枚举类型无法实现懒加载模式。

第二种饿汉式如下:

public class Singleton {
   //饿汉式如下:
    private static Singleton instance = new Singleton();
    
    private Singleton(){}
    public static Singleton getInstance(){
        return instance;
    }
}
  • 优点 :
    1.线程安全
    2.在类加载的同时已经创建好一个静态对象调用时反应速度快
  • 缺点 :
    资源效率不高,可能getInstance()永远不会执行到,但执行该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快醒醒鸭今天你编程了吗?

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值