单例模式

设计模式之单例模式

饿汉式(静态变量)

class Singleton1{
    //1.构造器私有化
    private Singleton1() {
    }
    private static final  Singleton1 singleton = new Singleton1();
    public static Singleton1 getInstance(){
        return singleton;
    }
}

优点:安全

缺点:可能造成内存浪费

饿汉式(静态代码块)

class Singleton2{
    //构造器私有化
    private Singleton2() {
    }
    private static Singleton2 singleton2;
    static{
        //在静态代码块中完成初始化
        singleton2 = new Singleton2();
    }
    public static  Singleton2 getInstance(){
        return singleton2;
    }
}

优缺点和上面一样

懒汉式(线程不安全)

/**
 * 懒汉式(线程不安全)
 */
public class TestSingleton3 {
    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            new Thread(() ->{
                System.out.println(Thread.currentThread().getName()+"@@"+Singleton3.getInstance().hashCode());
            },String.valueOf(i)).start();
        }
    }
}
class Singleton3{
    //构造器私有
    private Singleton3() {
    }
    private static Singleton3 singleton3;
    public static Singleton3 getInstance(){
        if(singleton3==null)
            singleton3 = new Singleton3();
        return singleton3;
    }
}

image-20200919201435002

优点:起到了懒加载的效果,但只能在单线程下使用

缺点:多线程下线程不安全

懒汉式(同步方法或同步代码块)

这里只演示同步方法

class Singleton4 {
    //构造器私有化
    private Singleton4() {
    }
    private static Singleton4 singleton4;
    public static synchronized Singleton4 getInstance() {
        if (singleton4 == null)
            singleton4 = new Singleton4();
        return singleton4;
    }
}

优点:多线程下安全

缺点:效率低下

DCL

class Singleton5{
    //构造器私有化
    private Singleton5() {
    }
    //注意要加volatile关键字
    //volatile关键字在这里的作用主要是保证可见性
    private static volatile Singleton5 singleton5;
    public static Singleton5 getInstance(){
        if (singleton5==null){
            synchronized (Singleton5.class){
                if(singleton5==null)
                    singleton5 = new Singleton5();
            }
        }
        return singleton5;
    }
}

优点:线程安全,延迟加载,效率较高

在这里说一下volatile的作用

volatile的三大作用:保证可见性,不保证原子性,禁止指令重排

笔者认为volatile在这里的作用主要是保证可见性,当一个线程进入到同步代码块中,它创建完对象后,应立刻对其它线程可见。volatile的

作用就是当被volatile修饰的变量发生变化时,其结果会立刻强制被刷回到主存中,而其它线程会通过总线嗅探机制和缓存一致性协议得知

自己缓存中的数据已经失效,会等待缓存行对应的主存地址被更新之后,再去读取对应的主存中的新值。

当然,这只是笔者认为的,网上的答案也是众说纷坛。笔者的答案只能作为参考,如有不对,请告知于我。

静态内部类

class Singleton6{
    //构造器私有化
    private Singleton6() {
    }
    //因为外部类加载的时候不会触发静态内部类的加载
    //并且类的静态属性只会在类第一次被加载的时候初始化,
    //所以在这里,JVM帮助我们保证了现成的安全性,在类进行初始化时,别的线程是无法进入的。
    private static class InnerSingleton{
        public static final Singleton6 INSTANCE = new Singleton6();
    }
    public static Singleton6 getInstance(){
        return InnerSingleton.INSTANCE;
    }
}

优点:线程安全,延迟加载,效率较高

枚举

enum  Singleton7{
    INSTANCE;
    public void method(){
        System.out.println("你好呀!!!");
    }
}

优点:线程安全,可以防止反序列化重新创建新的对象,推荐使用。

原文地址:https://www.cnblogs.com/gttttttt/p/13697519.html