单例模式
Posted wendy777
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式相关的知识,希望对你有一定的参考价值。
背景:单例模式模式是在编程中经常使用,他可以对需要使用的资金进行一次性初始化,防止多次初始化多次资源释放带来性能的开销。
最近在读《JAVA并发编程的艺术》发现有些知识点不错,整理出来。
单例模式常用模式是懒汉模式和饿汉模式
懒汉模式:就是用到时候才new出来。
饿汉模式:类一开始就加载好,可直接使用。
单线程情况下,我们会通过以下实现才生成一个懒汉模式的单例模式类。但是多线程访问时候,这种实现不满足要求。
public class Singleton { public static Singleton instance = null; public Singleton() { //todo } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
上面实现,如果在多线程情况下,可能出现多个线程同时访问instance == null 出现问题。接下来,介绍几种可以避免多线程竞争访问导致的问题。
(一)同步处理,延迟初始化,任何时刻只有一个线程抢占到访问资源。
public class Singleton { public static Singleton instance = null; public Singleton() { //todo } public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
但是这个方法也是弊端,由于采用同步处理,synchronized导致性能开销。如果getInstance()被多个线程频繁调用,会导致程序执行性能下降。反之,如果getInstance()不被多个线程频繁调用,那么这个延迟初始化将是不错的选择。
(二)双重检查锁,优化方案一出现多线程频繁调用instance(instance!=null)时阻塞的问题。
public class Singleton { public static Singleton instance = null; public Singleton() { //todo } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
优点:如果instance不为null, 多线程访问instance就不会出现阻塞,提高了性能,减少了开销。
缺点:由于JAVA内存模型,不同JIT编译器之间出现重排序问题,可能会出现线程A执行instance时instance==null,于是进入instance = new Singleton(), 线程B发现instance != null,但是这个instance可能未初始化。接下来我来细化下JMM执行过程方便理解。
instance = new Singleton() 可以分解成以下几步:
1. memory = allocate(); 分配对象的内存空间
2. ctorInstance(memory); 初始化对象
3. instance = memory; 设置instance指向刚分配的内存地址
由于在为伪代码中2和3之间可能会出现重排序. 引入《JAVA并发编程的艺术》的图。懒得自己画了。
鉴于这个问题,我们可以有如下更佳解决方式,把instance定义为volatile。JDK5之后引入volatile关键字,解决各个编译器处理器缓存和总线内存之间数据一致性问题。当把对象声明为volatile后,2和3重排序问题的问题,在多进程中被禁止,即不允许重排序。
public class Singleton { public static volatile Singleton instance = null; public Singleton() { //todo } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
(三)如果你使用Spring来管理bean, @Autowired注释本身就是单例模式,一开始就生成好一个bean实例,即为饿汉模式。
@Component public class Singleton { @Autowired public Singleton(....){ //TODO } }
以上是关于单例模式的主要内容,如果未能解决你的问题,请参考以下文章