单例模式中的多线程分析

编程技术  /  houtizong 发布于 3年前   65

谈到单例模式,我们立马会想到饿汉式和懒汉式加载,所谓饿汉式就是在创建类时就创建好了实例,懒汉式在获取实例时才去创建实例,即延迟加载。

饿汉式:

package com.bijian.study;public class Singleton {private Singleton() {}// 注意这是private 只供内部调用private static Singleton instance = new Singleton();// 这里提供了一个供外部访问本class的静态方法,可以直接访问  public static Singleton getInstance() {return instance;}}

懒汉式:

package com.bijian.study;public class Singleton {private static Singleton instance = null;public static synchronized Singleton getInstance() {// 这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     // 使用时生成实例,提高了效率!if (instance == null)instance = new Singleton();return instance;}}

     上面第二中形式的lazy initialization,也就是说第一次调用时初始Singleton,以后就不用再生成了。

注意到lazy initialization形式中的synchronized,这个synchronized很重要,如果没有synchronized,那么使用getInstance()是有可能得到多个Singleton实例。一般认为第一种形式要更加安全些。

       对于上面的懒汉式方式,从多线程角度来看,Synchronized放到方法上会影响性能。于是我们不难想到将其放到方法里。

package com.bijian.study;public class Singleton {private static Singleton instance = null;private static String lock = new String();public static Singleton getInstance() {// 这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     // 使用时生成实例,提高了效率!if (instance == null)synchronized(lock) {instance = new Singleton();}return instance;}}

我们稍加分析,不难发现,这样会存在线程安全问题,假如线程一刚好执行完if (instance == null)的判断语句(还未加锁),调度至线程二也执行这条判断语句,也是true,也进入了if的语句块中,这样就会产生两个实例,而非单实例了。

此时,我们稍加分析,那还不容易,在锁里面再加一个是否为空的判断,即所谓的double-checked locking (DCL),如下所示:

package com.bijian.study;public class Singleton {private static Singleton instance = null;private static String lock = new String();public static Singleton getInstance() {// 这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     // 使用时生成实例,提高了效率!if (instance == null)synchronized(lock) {if (instance == null) {instance = new Singleton();}}return instance;}} 

     此时,很多人都会觉得无懈可击了。从多线程角度来看,这样写的单例确实没有问题了,但从Java的类创建原理来看,可能还有问题。从浅显简单的理解来看,就是对象还未完全创建出来,但instance变量已被赋值,此时另一个线程获取实例时,会得到instance,但它的堆空间及相关的方法还未完成时,调用实例方法就会出错。

       啊?还存在这样的问题呀?这可以虚拟机的实现问题,难道还要我考虑?是的,其实稍加改动,就可以避免这样的问题。

       解决办法,加一个局部变量,如下所示:

package com.bijian.study;public class Singleton {private static Singleton instance = null;private static String lock = new String();public static Singleton getInstance() {// 这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     // 使用时生成实例,提高了效率!if (instance == null)synchronized(lock) {if (instance == null) {Singleton temp = new Singleton();instance = temp;}}return instance;}}

 

进一步深入可参考:

Double-checked locking and the Singleton pattern

When is a singleton not a singleton?

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!

留言需要登陆哦

技术博客集 - 网站简介:
前后端技术:
后端基于Hyperf2.1框架开发,前端使用Bootstrap可视化布局系统生成

网站主要作用:
1.编程技术分享及讨论交流,内置聊天系统;
2.测试交流框架问题,比如:Hyperf、Laravel、TP、beego;
3.本站数据是基于大数据采集等爬虫技术为基础助力分享知识,如有侵权请发邮件到站长邮箱,站长会尽快处理;
4.站长邮箱:[email protected];

      订阅博客周刊 去订阅

文章归档

文章标签

友情链接

Auther ·HouTiZong
侯体宗的博客
© 2020 zongscan.com
版权所有ICP证 : 粤ICP备20027696号
PHP交流群 也可以扫右边的二维码
侯体宗的博客