设计模式之单例模式

设计模式之单例模式

一、单例模式定义

定义:“一个类仅有一个实例,并且自行实例化向整个系统提供”

二、实现思路:

(1)构造器私有化,防止外界通过调用构造方法实例化对象。

(2)提供一个公有的静态方法,只有通过该类提供的静态方法才能得到该类的唯一实例

三、实现单例模式的几种方法

1、饿汉式(线程安全)

/**
 * @Created with IDEA
 * @author:Dick_YangDi
 * @Date:2018/4/24
 * @Time:12:44
 * @JDK versions: 1.8.0_101
 */
public class Singleton {
    private final static Singleton instance = new Singleton();

    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
    //...
}

优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。 

缺点:由于在类装载的时候就完成实例化。所以在程序运行时如果从始至终从未使用过这个实例,则会造成内存的浪费。

2、懒汉式(线程不安全)

/**
 * @Created with IDEA
 * @author:Dick_YangDi
 * @Date:2018/4/24
 * @Time:12:56
 * @JDK versions: 1.8.0_101
 */
public class Singleton {
    private static Singleton singleton;

    private Singleton(){}

    public static Singleton getInstance(){
        if(singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
    //...
}

优点:实现了延迟加载,避免了内存的浪费

缺点:线程不安全。在多线程下两个线程同时通过了if判断,则会产生多个实例。所以并不推荐使用

3、懒汉式2.0(线程安全)

/**
 * @Created with IDEA
 * @author:Dick_YangDi
 * @Date:2018/4/24
 * @Time:13:04
 * @JDK versions: 1.8.0_101
 */
public class Singleton {
    private static Singleton singleton;

    private Singleton(){}

    public static synchronized Singleton getInstance(){
        if(singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
    //...
}

优点:延迟加载且线程安全

缺点:效率低下,每个线程想获得类的实例时,不论是否初始化都要进行同步,不推荐使用

4、双重锁校验(线程不安全)

/**
 * @Created with IDEA
 * @author:Dick_YangDi
 * @Date:2018/4/24
 * @Time:13:15
 * @JDK versions: 1.8.0_101
 */
public class Singleton {
    private static Singleton singleton;

    private Singleton() {}

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

优点:延迟加载,且效率高。

缺点:指令重排序导致多线程访问时获得没初始化的对象,所以不推荐使用

        创建一个对象的正常过程为:①分配对象内存空间->②初始化对象->③设置引用指向内存空间。但是jvm会做出优化将②和③的执行次序交换。下面我们用A和B来模拟两个线程的执行情况

时间                                         线程A                                                                         线程B                        
t1 A1:   分配对象的内存空间  
t2 A3:设置instance指向内存空间  
t3   B1:判断instance是否为null
t4   B2:由于instance不为null,线程B将访问instance引用的对象
t5 A2:初始化对象  
t6 A4:访问instance引用对象  
       由上图可以看到,jvm进行指令重排序进行优化的时候将A2和A3的执行次序交换。导致线程B使用了一个未初始化的对象的引用。导致程序运行时出现异常。

5、双重锁校验2.0(线程安全)

/**
 * @Created with IDEA
 * @author:Dick_YangDi
 * @Date:2018/4/24
 * @Time:13:15
 * @JDK versions: 1.8.0_101
 */
public class Singleton {
    private volatile static Singleton singleton;

    private Singleton() {
    }

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

            延迟加载,且效率高。且解决了指令重排序的问题,可以使用

6、静态内部类(线程安全)

/**
 * @Created with IDEA
 * @author:Dick_YangDi
 * @Date:2018/4/24
 * @Time:13:41
 * @JDK versions: 1.8.0_101
 */
public class Singleton {
    private Singleton(){}

    private static class SingletonHolder{
        private static final Singleton SINGLETON = new Singleton();
    }

    public static Singleton getInstance(){
        return SingletonHolder.SINGLETON;
    }
    //...
}

        静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonHolder类,从而完成Singleton的实例化。 
           类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。这一技术是被JVM明确说明了的,因此不存在任何二义性。

7、枚举法(线程安全)

/**
 * @Created with IDEA
 * @author:Dick_YangDi
 * @Date:2018/4/24
 * @Time:13:45
 * @JDK versions: 1.8.0_101
 */
public enum Singleton {
    INSTANCE;
    private Singleton() {}
}

功能完整,使用简洁、无常提供了序列化,还可以防止反射攻击,单元素的枚举类型是实现单例模式的最佳方法


你可能感兴趣的:(设计模式之单例模式)