设计模式之单例模式

1.饿汉模式

//饿汉模式
public class Singleton1 {
    private static Singleton1 singleton1 = new Singleton1();  //饿汉嘛,很着急,所以在类加载时就进行初始化

    private Singleton1() {
    }

    public static Singleton1 getSingleton1() {
        return singleton1;
    }
}

饿汉模式是线程安全的,因为 private static Singleton1 singleton1 = new Singleton1(); 语句是在类加载时完成的,具体是在类加载的初始化阶段时的<client>方法中进行的。

2.懒汉模式

//懒汉模式
public class Singleton2 {
    private static Singleton2 singleton2;

    private Singleton2() {
    }

    public static synchronized Singleton2 getSingleton2() {      //懒汉嘛,很懒,所以啥也不考虑,直接给方法上加synchronized来解决问题(并发时效率很低)
        if (singleton2 == null) {
            singleton2 = new Singleton2();
        }
        return singleton2;
    }
}

这种懒汉模式是线程安全的,在这里只有获取到Class的对象锁才会进入到方法。

3.双重检测模式

//双重检测模式(可以理解为比懒汉勤快的一种模式。。。)
public class Singleton3 {
    private static volatile Singleton3 singleton3;

    private Singleton3() {
    }          

    public static Singleton3 getSingleton3() {               //不在方法上加synchronized,
        if (singleton3 == null) {            //先判断是否null,为null时才回去加锁
            synchronized (Singleton3.class) {    //这是只有在单例还没有被初始化时才需要进行加锁
                if (singleton3 == null) {      //再一次进行检测,这里主要是因为可能在对象还没初始化时,可能已经有若干个线程已经加入到监视器锁中了,所以这里还要进行一次检测,防止产生多个实例
                    singleton3 = new Singleton3();
                }
            }
        }
        return singleton3;
    }
}

(1)在使用volatile后为线程安全的,volatile用来禁止指令重排序

(2)为什么要使用volatile关键字

  由于 singleton3 = new Singleton3();是分为三步来进行的

  ①为对象分配内存空间

  ②初始化对象

  ③将对象复制给引用

  由于代码在执行过程可能出现指令重排序,那么执行步骤可能会变成①-③-②,那么当线程A完成①③操作,B线程进入方法判断不为空,将会获取到这个没有初始化完成的对象。

  而使用volatile后禁止这三条指令的重排序。保证在singleton3在不为空是对象已经被初始化。

4.静态内部类

//静态内部类
public class Singleton4 {
    private Singleton4() {
    }

    private static class SingletonHolder {            //在静态内部类中去初始化单例
        private static Singleton4 singleton4 = new Singleton4();
    }

    public static Singleton4 getSingleton4() {        
        return SingletonHolder.singleton4;      //通过静态内部类来获取
    }
}

(1).该实现方式是线程安全的。首先我们看看java中类的初始化时机

  其中之一为:在调用类读取或设置一个类的静态字段的时候。

      所以只有我们在第一次调用getSingleton4()方法时,SingletonHolder才会被初始化。

  

关于以上代码可在https://github.com/LiWangCai/blogRelated中获取

 

posted @ 2019-04-22 16:16 学习使我快乐。 阅读(...) 评论(...) 编辑 收藏