Chapter 1 单例设计模式

来源:互联网 发布:北宋灭亡原因知乎 编辑:程序博客网 时间:2024/05/31 13:17

1 概述

Java中单例设计模式是一种常见的设计模式,单例模式确保某个类在内存中只有一个实例。单例设计模式的应用在计算机系统中非常常见,例如,缓存、日志、线程池等都经常应用到该模式。在spring框架中,默认的bean的注入方式是单例式的,这样可以保证用户在引用bean的对象时不会重复的生成实例。

单例设计模式一般有以下特点:(1)单例类的内部会生成唯一的对象;(2)用户无法在类的外部生成对象,也就是构造函数会被private修饰;(3)单例类会对外提供一个接口,供外部访问单例对象。其有多种的实现方式,包括饿汉式,懒汉式,静态内部类等。

2 各种实现方式

2.1 饿汉式

饿汉式的单例设计模式的实现非常的直接明了,在声明单例类的引用时即生成一个该类对象:

/** * Created by fubinhe on 16/12/5. */public class Singleton {    private static final Singleton INSTANCE = new Singleton();    private Singleton() {}    public static Singleton getInstance() {        return INSTANCE;    }}

2.2 懒汉式

懒汉式的单例实现和饿汉式的差别在于单例对象的加载时机的不同。它不会在声明单例引用的时候立即加载该对象,而是在用户尝试获得该对象的引用的时候先判断该对象是否已经生成,如果没有生成则加载该对象,起到延迟加载的目的:

/** * Created by fubinhe on 16/12/5. */public class Singleton {    private static Singleton instance;    private Singleton() {}    public static Singleton getInstance() {        if (instance == null) {            instance = new Singleton();        }        return instance;    }}

但是,这里有一个安全性的问题。假设某个线程在执行第11行代码时发现该对象的引用为空,则会试图生成该对象。然而该对象还没有生成成功时,另外又有线程执行到第11行代码,判断条件依然成立,它也会去生成一个对象。这样,内存中就会出现不知一个实例,显然违背了单例设计模式的初衷。

为此,有人想到给getInstance方法加锁以克服并发问题,加锁的方式也很简单,就是用synchronized关键字修饰getInstance方法:

/** * Created by fubinhe on 16/12/5. */public class Singleton {    private static Singleton instance;    private Singleton() {}    public static synchronized Singleton getInstance() {        if (instance == null) {            instance = new Singleton();        }        return instance;    }}
但是,这种方式又有效率上的问题,就是每个线程想要拿到单例对象的引用时都必须尝试获得锁(即Singleton的字节码对象,Singleton.Class)。为了提高效率,又有人提出了DLC(double lock checking)的方法,通过两次判断的方法,减少锁的竞争次数。
/** * Created by fubinhe on 16/12/5. */public class Singleton {    private static volatile Singleton instance;    private Singleton() {}    public static Singleton getInstance() {        if (instance == null) {            synchronized (Singleton.class) {                if (instance == null) {                    instance = new Singleton();                }            }        }        return instance;    }}

这样,当单例对象生成以后,线程想要获得该对象的引用时,再也不需要尝试获得锁,因此效率大大提高。其中,单例对象使用volatile关键字进行了修饰,是为了提高该单例对象的可见性,即某个线程成功创建该对象以后其他线程能立即看到。

2.3 静态内部类

如果想做到延迟生成单例对象,并且不想加锁,可以使用静态内部类的方式:

/** * Created by fubinhe on 16/12/5. */public class Singleton {    private static class LazyHolder {        private static final Singleton INSTANCE = new Singleton();    }    private Singleton() {}    public static Singleton getInstance() {        return LazyHolder.INSTANCE;    }}

该方法的主要原理是:在静态内部类(私有的)内部创建单例对象,那么只有在用户首次尝试获得该对象的引用时,静态内部类才会被加载,因此能起到延迟加载的效果。同时由于单例对象只会在加载静态内部类时才会被创建,因此也不会有线程安全的问题。

3 总结

单例设计模式在所有的设计模式中是最基础的一种,实现原理也比较简单,但是各种方法的优缺点还是值得学习和研究一下的。其实除了以上常见的三种方式,还有一种所谓的登记式的实现方法,其实其原理也很简单,就是利用一个Map容器,通过这个Map容器判断单例对象是否被创建。





0 0