单例模式学习

package com.test.mytest;

/**
 * Created by wuzm on 2017/11/24.
 */

/**
 * 单例模式
 */
public class Singleton1 {
    private static Singleton1 singleton = null;

    private Singleton1() {

    }
    /**
     * 饱汉模式
     * 等第一次使用的时候在初始化(已吃饱,等饿的时候再吃)--懒加载
     * 优点:启动速度快,节省资源
     * 缺点:线程不安全,存在竞态条件
     */
    public static Singleton1 getInstance() {
        if (singleton == null) {
            singleton = new Singleton1();
        }
        return singleton;
    }

    /**
     * 饱汉变种1,解决线程不安全问题
     * 优点:绝对线程安全
     * 缺点:并发性能极差,任何一个线程占用了getInstance_1方法时,别的线程都只能等待,其实已经沦为串行的了
     */
    public synchronized static Singleton1 getInstance_1() {
        if (singleton == null) {
            singleton = new Singleton1();
        }
        return singleton;
    }
    /**
     * 饱汉变种2:双重检查
     * 即在变种1的基础上又套了一层check
     * 优点:懒加载+线程安全
     * 缺点:有可能得到半个对象?
     * ----------------------------------------------------------------------------------------
     * 解析:
     * 假设有A,B两个线程,A获得同步锁,执行single=new Singleton1(),该行代码其中有以下3个步骤(执行无序):
     * (1)memory=allocate();//为singleton对象分配内存
     * (2)instance=memory;//注意现在instance是非空的,但还没初始化
     * (3)ctorSingleton(instance);//调用singleton的构造函数,传递instance
     * (4)连接引用和实例
     * 当执行到instance=memory;时,线程B进入了第一次null判断,判断结果为非空,返回了instance,
     * 但此时返回的不是单例的实例对象,而是内存对象
     * -----------------------------------------------------------------------------------------
     * 为什么要在synchronized里面再加一层if判断呢?
     * -----------------------------------------------------------------------------------------
     * 解析:
     * 假设线程A,B同时执行到了synchronized,虽然只有一个线程能执行,假设A先执行,A执行完成后创建了一个实例
     * 之后线程B获得了锁,但是此时没有检查singleton是否为空就直接执行了,所以还是会出现两个single实例的情况
     */
    public static Singleton1 getInstance_2() {
        if (singleton == null) {
            synchronized (Singleton1.class){
                if (singleton == null) {
                    singleton = new Singleton1();
                }
            }
        }
        return singleton;
    }
    /**
     * 饱汉变种3:解决变种2中半个对象问题
     * volatile:禁止指令重排序,保证内存可见性
     * 禁止指令重排序:就是解决上面提到的返回内存对象问题
     * private static volatile Singleton1 singleton = null;
     * 还有一种方法可以不使用volatile达到同样的效果
     * Singleton1 temp=new Singleton1();
     * temp.toString();//构造与赋值之间随意做点事情保证顺序
     * singleton=temp;
     */



    private static final Singleton1 singleton1 = new Singleton1();
    /**
     * 饿汉模式
     * 类加载时初始化单例,以后访问时直接返回
     * 优点:天生的线程安全,得益于类加载机制
     * 确定:资源浪费,万一单例没有用武之地
     */
    public static Singleton1 getInstance_3() {
        return singleton1;
    }

    /**
     * Holder模式
     * 既希望利用饿汉模式的有希望通过懒加载避免资源浪费
     * 通过静态的Holder类持有真正的实例,间接实现了懒加载
     */
    private static class SingletonHolder {
        private static final Singleton1 singleton = new Singleton1();
        private SingletonHolder() {
        }
    }
    public synchronized static Singleton1 getInstance_4() {
        return SingletonHolder.singleton;
    }
}

猜你喜欢

转载自blog.csdn.net/wuzhimin15181111/article/details/78626512