背景:单例模式模式是在编程中常常使用,他能够对须要使用的资金进行一次性初始化,防止屡次初始化屡次资源释放带来性能的开销。编程
最近在读《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 出现问题。接下来,介绍几种能够避免多线程竞争访问致使的问题。spa
(一)同步处理,延迟初始化,任什么时候刻只有一个线程抢占到访问资源。线程
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()不被多个线程频繁调用,那么这个延迟初始化将是不错的选择。code
(二)双重检查锁,优化方案一出现多线程频繁调用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 } }