设计模式之单例模式

单例模式解释

单例模式是一种对象创建性模式,使用单例模式,可以保证为一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象。

单例模式的要点有三个:一是某个类只能有一个实例;而是必须自行创建整个实例;三是它必须自行向整个系统提供整个实例。

英文定义为:Ensure a class only has one instance, and provide a global point of access to it.

单例模式深入分析

单例模式适合一个类只有一个实例的情况, 比如窗口管理器,打印缓冲池和文件系统,它们都是原型的例子。典型的情况是,那些对象的类型被遍及一个软件系统的不同对象访问,因此需要一个全局的访问指针,这便是总所周知的单例模式的应用。当然这只有在你确信你不再需要任何多于一个的实例的情况下。

使用场景及代码实现

饿汉式1——线程安全

/**
 * 饿汉式
 * 在类装载时就创建实例,但是有两个问题:
 * 1. 如果实例化过程中涉及大量的操作时会导致类装载慢,影响整个装载过程
 * 2. 如果创建的对象并没有得到应用,将造成资源的浪费
 */
@Slf4j
@ThreadSafe
public class HungrySingleton1 {

    // 私有构造方法
    private HungrySingleton1() {
        // 例如以下可能导致类装载过慢
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("HungrySingleton1");
    }

    // 单例实例
    private static HungrySingleton1 instance = new HungrySingleton1();

    // 静态的工厂方法
    public static HungrySingleton1 getInstance() {
        return instance;
    }
}

饿汉式2——线程安全

/**
 * 饿汉式
 * 将实例化对象放到静态代码块中
 */
@Slf4j
@ThreadSafe
public class HungrySingleton2 {

    // 私有构造方法
    private HungrySingleton2() {
        log.info("HungrySingleton2");
    }
    // 单例实例,必须放到静态代码块之前,非实例化出来的对象为null
    private static HungrySingleton2 instance = null;

    static {
        instance = new HungrySingleton2();
    }

    // 静态的工厂方法
    public static HungrySingleton2 getInstance() {
        return instance;
    }
}

懒汉式1——线程非安全

/**
 * 懒汉式
 * 这是线程不安全的,原因是多个线程可能同时判断instance是否为null,然后同时创建对象并返回不同的对象
 */
@Slf4j
@NotThreadSafe
public class LazySingleton1 {
    // 私有构造方法
    private LazySingleton1() {
        log.info("LazySingleton1");
    }
    // 单例实例
    private static LazySingleton1 instance = null;

    // 静态的工厂方法
    public static LazySingleton1 getInstance() {
        if (null == instance) {
            instance = new LazySingleton1();
        }
        return instance;
    }
}

懒汉式2——线程安全

/**
 * 懒汉式
 * 这是线程安全的,但是可能造成大量线程的阻塞,不推荐使用这种方式
 */
@Slf4j
@ThreadSafe
@NotRecommend
public class LazySingleton2 {
    private LazySingleton2() {
        log.info("LazySingleton2");
    }

    private static LazySingleton2 instance = null;

    public static synchronized LazySingleton2 getInstance() {
        if (null == instance) {
            instance = new LazySingleton2();
        }
        return instance;
    }
}

懒汉式3——线程非安全

/**
 * 懒汉式
 * 双重判断同步锁,但是是线程不安全的,分析一下实例化的过程
 * 1. memory = allocate() 分配对象的内存空间
 * 2. ctorInstance() 初始化对象
 * 3. instance = memory 设置instance指向刚分配的内存
 *
 * 但是,此时JVM和CPU优化,发生指令重排,导致上面的执行顺序可能按照1、3、2进行
 * 因此,当一个线程按照1、3、2进行了,另一个线程得到的实例可能是一个空指针
 */
@Slf4j
@NotThreadSafe
public class LazySingleton3 {
    private int i = 1;
    private LazySingleton3() {

        log.info("LazySingleton3");
    }

    private static LazySingleton3 instance = null;

    public static LazySingleton3 getInstance() {
        if (null == instance) {
            synchronized(LazySingleton3.class){// 同步锁
                if (null == instance) {
                    instance = new LazySingleton3();
                }
            }
        }else {
            log.info("{}", instance.i++);
        }

        return instance;
    }
}

懒汉式4——线程安全

/**
 * 懒汉式,推荐使用
 * 实例化的过程
 * 1. 向内存申请内存空间
 * 2. 创建实例
 * 3. 将创建的实例指向刚刚申请的内存空间
 *
 * 为了避免JVM指令重排,给实例加上volatile
 */
@Slf4j
@ThreadSafe
public class LazySingleton4 {
    private LazySingleton4() {
        log.info("LazySingleton4");
    }

    // volatile + 双重判断同步锁 -> 禁止指令重排,实现线程安全
    private volatile static LazySingleton4 instance = null;

    public static LazySingleton4 getInstance() {
        if (null == instance) {
            synchronized(LazySingleton4.class){ // 同步锁
                if (null == instance) {
                    instance = new LazySingleton4();
                }
            }
        }
        return instance;
    }
}

 

posted @ 2015-11-06 14:01  幺刀  阅读(91)  评论(0编辑  收藏  举报