详解设计模式之单例模式

1、�什么是单例?

单例顾名思义就是单个实例。日常工作都是自由的通过构造方法创建对象的,所以听到单例时,我们应该意识到其下是暗含了2层意思的,①构造方法不在为你所用,你不需要知道如何创建对象。②我会给你提供获取实例的方法,不需要你自己动手创建。

单例示意图.png

不清楚uml类关系的请移步UML--类图详解

2、�什么情况下使用单例?

当你需要内存中只有一个对象的时候。

3、为什么使用单例?

单例保证了内存中数据的唯一性,同时也降低了内存的开销。

3、�如何实现单例?

在1中了解到两层含义后,对我们创建单例类是有帮助的,总结下就是:①私有化构造方法 ②对外提供获取实例方法

  • 饿汉式
    所谓饿汉式,就是在类加载的时候就迫不及待的创建对象,等到调用获取实例方法直接拿到对象返回即可。
    看实现代码:
public class HungarySingleton {
    private static HungarySingleton intance = new HungarySingleton();
    
    private HungarySingleton(){
        
    }
    public static HungarySingleton getInstance(){
        return intance;
    }
}

懒汉式是最简单的单例模式,在类加载的时候就实例化,避免了多线程问题的同时保证了实例唯一性。缺点是即使不需要时候也会被实例,会占用内存。所以推荐在实例占用内存不多的情况下使用,否则请使用下面几种方式。

  • 懒汉式
    懒汉式,就是在首次调用的时候才会创建实例,相比饿汉式,延迟了初始化。
public class LayzySingleton {
    private static LayzySingleton instance = null;

    private LayzySingleton() {

    }
    public LayzySingleton getInstatnce() {
        if (null == instance) {
            instance = new LayzySingleton();
        }
        return instance;
    }
}

上面代码有个问题,就是在多线程调用中会有产生多个实例的隐患,所以需要使用线程同步:

public class LayzySingleton {
    private static LayzySingleton instance = null;

    private LayzySingleton() {

    }
    public synchronized LayzySingleton getInstatnce() {
        if (null == instance) {
            instance = new LayzySingleton();
        }
        return instance;
    }
}

synchronized的加入虽然解决了多实例的隐患,但是又带来了性能低下的问题,因为我们只需要在首次创建对象时让同步产生作用即可,其后的调用无需同步,现在只要是调用该方法就同步,看来还需要改进
下面使用双重校验加锁(DCL):

public class LayzySingleton {
    private static LayzySingleton instance = null;

    private LayzySingleton() {

    }
    public  LayzySingleton getInstatnce() {
        if (null == instance) {
            synchronized(LayzySingleton.class){
                if (null == instance) {
                    instance = new LayzySingleton();
                }
            }
        }
        return instance;
    }
}

现在多线程问题解决了,性能也得到优化了是不是双重校验加锁就完美了呢?
NO~
在Java并发编程中的指令重排序中有讲到双重校验加锁失效问题。
要禁止指令重排序需要使用volatile修饰变量

  private volatile static LayzySingleton instance = null;
  • 静态内部类
    如果觉得饿汉式占内存,懒汉式又要考虑多线程问题,那么可以使用静态内部类可能是你想要的:
public class Singleton {
    
    private Singleton(){
        
    }
    public Singleton getInstance(){
        return Holder.instance;
    }
    private class Holder{
        private  static final  Singleton instance = new Singleton();
    }
}

静态内部类避免了加载Singleton类时就初始化问题,只有在调用getInstance时才会致使Holder类被加载并初始化,同时也避免了线程安全问题和性能问题,推荐使用此方法。

  • 枚举式
    如果觉得上面的还是麻烦,可以使用枚举单例,
    代码实现:
public enum EnumSingleton {
    
    INSTANCE ;
    
    int num = 33;
    String config = "jenson";
    
    public void doSomething(){
        
    }
}

枚举单例也可以有属性可以有方法,重要的是枚举默认就是线程安全的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容