单例模式最佳实践

2019-04-29  本文已影响0人  YyangSir

简单介绍

单例模式是最简单的设计模式之一,提供了一种创建对象的方式,确保在整个系统中只有一个对象被创建.单例模式解决了频繁创建重复对象的问题节约资源,可以省略创建对象所需要花费的时间,对于一些重量级对象而言这点是很重要的.并且因为不需要频繁创建对象 GC 的压力也会有所减轻.

单例模式的一些实现方式

通常来说在 Java 中的单例模式分为饿汉式和懒汉式,而且单例类需要一个 private 的构造函数防止被其他代码实例化.下面来具体说一下java 中单例模式的实现.

饿汉式

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

饿汉式的单例模式代码简单,线程安全,通过classLoader机制保证了单例对象的唯一性 但是不能确保instance 是在调用getInstance()方法的时候生成的不能达到懒加载效果

懒汉式

public class Singleton{
    private static Singleton instance;
    private Singleton(){}
    //加入 synchronize 保证线程安全
    public synchronized static Singleton getInstance(){
        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

在获取的时候创建实例可以达到延迟加载的效果并且加入了 synchronize 保证线程安全,但每次调用代码的时候都要加锁,性能比较低还有可能发生阻塞

DCL双重校验锁

public class Singleton{
    //volatile防止指令重排序
    private static volatile Singleton instance=null;
    
    private Singleton(){}
    
    public static Singleton getInstance(){
        if(instance==null){
            synchronized(Singleton.class){
                //加入第二次校验
                if(instance==null){
                    instance=new Singleton();
                }
            }
        } 
        return instance;
    }
}

双重校验锁就是为了解决上述问题而存在的,先检查实例是否存在然后再去创建,可以不用每次调用方法都获取同步锁性能会有一些提升,减小的锁的颗粒度.但是 java 对象的创建和赋值不是一步操作的,有可能先去赋值给中instance之后才去创建 Singleton 这时添加volatile关键字防止指令重排序解决了这个问题.

静态内部类

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

使用静态内部类时,当Singleton被创建的时候不会去加载SingleHoler,只有第一次调用getInstance()方法时才回去创建instance,加载SingleHoler将常量池中的符号引用替换成直接引用,这种方式不仅保证了线程安全而且可以达到延迟加载的效果.

最佳实践

public enum Singleton{
    INSTANCE;
    public void print(){
        System.out.println("快乐就完事了!");
    }
}

这种方法在功能上与公有域方法相近,但是它更加简洁,无偿提供了序列化机制,绝对防止多次实例化,即使是在面对复杂序列化或者反射攻击的时候。虽然这种方法还没有广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。 --《Effective Java 中文版 第二版》

简单到不能再简单了啊.jvm 在加载枚举类的时候会使用loadClass方法使用同步代码块解决线程安全问题.使用 enum 的单例模式还能避免反序列化破坏单例并且不能被反射攻击.

上一篇下一篇

猜你喜欢

热点阅读