单例模式(懒汉模式)

package com.exam.test;

/**
 * 懒汉式单例
 * Created by xiangyang.laixiang on 2016/8/23.
 */
public class SingleInstance {
    private static SingleInstance singleInstance;

    /**
     * 懒汉式单例,非线程安全
     * @return
     */
    public static SingleInstance getSingleInstance(){
        if(singleInstance == null)
        {
            singleInstance = new  SingleInstance();
        }
        return singleInstance;
    }

    /**
     * 懒汉式单例,线程安全
     * 这是实现线程安全最简单的方式,但这里会导致一个性能问题,因为我们把整个getInstance同步起来了,
     * 这就会导致每时每刻都只能有一个线程调用getInstance方法,而同步只发生在第一次声明的时候
     * 这就引出了双重检验锁的概念
     * @return
     */
    public synchronized static SingleInstance getSingleInstance2(){
        if(singleInstance == null)
        {
            singleInstance = new  SingleInstance();
        }
        return singleInstance;
    }
}


/**
 * 双重检验锁
 */
class DoubleCheckSingleInstance{
    //使用volatile关键字修饰
    //private static DoubleCheckSingleInstance singleInstance;
    private static DoubleCheckSingleInstance singleInstance;
    public static DoubleCheckSingleInstance getSingleInstance()
    {
        if(singleInstance == null)
        {
            synchronized (DoubleCheckSingleInstance.class)
            {
                if(singleInstance == null)
                {
                    singleInstance = new DoubleCheckSingleInstance();
                }
            }
        }
        return singleInstance;
    }

    /**
     * 上述这段代码看起来很完美,但是中间存在着一个问题那就是 new DoubleCheckSingleInstance()
     * 这个操作不是原子操作,大致有三步构成。
     * 1. 给instance分配内存
     * 2. 调用DoubleCheckSingleInstance构造函数初始化成员变量
     * 3. 将instance指向内存
     * 问题就出在这一部分,jvm即时编译器中存在着指令重排序的优化。如果1,2,3的执行步骤不会存在问题
     * 倘若1,3,2
     * 那么当线程二执行完第三步以后,线程三抢占cpu资源,则进行检查,发现instance不为空,则返回,但此时
     * 成员变量尚未初始化,则会发生调用错误。
     * 解决方案是使用volatile来修饰singleInstance来强制每次都从内存映像中读取数据,其实volitile也可以起到
     * 禁止重排序的功能,在java1.5以后使用比较安全。(1.5之前的内存模型是存在问题的)
     */
}

/**
 * effective java中推荐的内部类实现单例的写法 。
 */
class SingtonInstance{
    private SingtonInstance()
    {
        System.out.println("constructor");
    }
    private static class SingtonHolder{
        private static SingtonInstance instance = new SingtonInstance();
    }
    public static void beforeInvoke()
    {
        System.out.println("before invoke");
    }

    public static SingtonInstance getInstance()
    {
        /**
         * 此处最开始理解有点绕,但是经过我对象的一个提醒恍然明白,对于内部类的所有对象对于其
         * 外部类来说都是可见的,简直是霸气,否则private的可见范围是不允许直接访问的。
         */
        return SingtonHolder.instance;
    }
}

public class Test {
    public static void main(String[] args) {
        SingtonInstance.beforeInvoke();
        SingtonInstance.getInstance().beforeInvoke();
    }
}

你可能感兴趣的:(单例模式(懒汉模式))