单例模式

一个类只有一个实例,并提供一个全局访问此实例的点,哪怕多线程同时访问。

单例模式主要解决了一个全局使用的类被频繁的创建和消费的问题。

单例模式的案例场景

  • 数据库的连接池不会反复创建
  • spring中一个单例模式bean的生成和使用
  • 在我们平常的代码中需要设置全局的一些属性保存

七种单例模式的实现1.懒汉模式(线程不安全,懒加载)

public static class Singleton_01 {    private static Singleton_01 instance;        private Singleton_01() {}        public static Singleton_01 getInstance() {        if (null != instance) {            return instance;        }        instance = new Singleton_01();        return instance;    }}

非常标准且简单地单例模式。

满足了懒加载,但是线程不安全。

2.懒汉模式(线程安全,懒加载)

public static class Singleton_02 {    private static Singleton_02 instance;        private Singleton_02() {}        public static synchronized Singleton_02 getInstance() {        if (null != instance) {            return instance;        }        instance = new Singleton_02();        return instance;    }}

线程安全,但是效率低,不建议使用。

3.饿汉模式(线程安全)

public class Singleton_03 {    private static Singleton_03 instance = new Singleton_03();        private Singleton_03() { }        public static Singleton_03 getInstance() {        return instance;    }   }

饿汉模式,在程序启动的时候直接运行加载。

4.使用内部静态类(线程安全、懒加载)

public class Singleton_04 {    private static class SingletonHolder {        private static Singleton_04 instance = new Singleton_04();    }        private Singleton_04() { }        public static Singleton_04 getInstance() {        return SingletonHolder.instance;    }}

通过内部静态类实现了懒加载和线程安全,推荐使用。

5.锁+双重校验(线程安全,懒加载)

private class Singleton_05 {    private static volatile Singleton_05 instance;        private Singleton_05() { }        public static Singleton_05 getInstance() {        if (null != instance) {            return instance;        }        synchronized (Singleton_05.class) {            if (null == instance) {                instance = new Singleton_05();            }        }        return instance;    }}

相较于线程安全的懒汉模式,这种方法效率更高。

6.CAS【AtomicReference】(懒加载,线程安全)

public class Singleton_06 {    private static final AtomicReference INSTANCE = new AtomicReference();        private Singleton_06() {  }        public static final Singleton_06 getInstance() {        for(;;) {            Singleton_06 instance = INSTANCE.get();            if (null != instance) {                return instance;            }            INSTANCE.compareAndSet(null, new Singleton_06());            return INSTANCE.get();        }    }}

AtomicReference可以封装引用一个V实例,支持并发访问。

使用CAS的好处就是不需要使用传统的加锁方式保证线程安全,而是依赖于CAS的忙等算法,依赖于底层硬件的实现,来保证线程安全。相对于其他锁的实现,没有线程的切换和阻塞,也就没有了额外的开销,并且可以迟迟较大的并发性。

但CAS的一个缺点就是忙等,如果一直没有获取到将会处于死循环中。

7.枚举单例(线程安全)

public enum Singleton_07 {        INSTANCE;        private Map cache = new ConcurrentHashMap();    public Map getCache() {        return cache;    }    public void addToCache(String key, String value) {        cache.put(key, value);    }}

《Effective Java》作者推荐使用枚举的方式解决单例模式,这种方法解决了线程安全、自由串行化和单一实例的问题。

调用时也很方便:Singleton_07.INSTANCE.getCache();

枚举单例十分简洁,并且它无常地提供了串行化机制,绝对防止对此实例化(毕竟是枚举)。

但是它在继承的场景下是不可使用的,并且不是懒加载(毕竟是枚举)。