[转帖]单例模式中的多线程分析_Android, Python及开发编程讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  Android, Python及开发编程讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 3041 | 回复: 0   主题: [转帖]单例模式中的多线程分析        下一篇 
只是很无聊
注册用户
等级:中尉
经验:440
发帖:33
精华:0
注册:2013-6-18
状态:离线
发送短消息息给只是很无聊 加好友    发送短消息息给只是很无聊 发消息
发表于: IP:您无权察看 2013-6-19 10:02:45 | [全部帖] [楼主帖] 楼主

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

饿汉式:

Java代码  
北京联动北方科技有限公司

  1. package com.bijian.study; 
  2. public class Singleton { 
  3.       
  4.        private Singleton() { 
  5.        } 
  6.       
  7.        // 注意这是private 只供内部调用 
  8.        private static Singleton instance = new Singleton(); 
  9.       
  10.        // 这里提供了一个供外部访问本class的静态方法,可以直接访问 
  11.        public static Singleton getInstance() { 
  12.              return instance; 
  13.        } 


懒汉式:

Java代码  
北京联动北方科技有限公司

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


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

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

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

Java代码  
北京联动北方科技有限公司

  1. package com.bijian.study; 
  2. public class Singleton { 
  3.       
  4.        private static Singleton instance = null; 
  5.        private static String lock = new String(); 
  6.       
  7.        public static Singleton getInstance() { 
  8.             
  9.              // 这个方法比上面有所改进,不用每次都进行生成对象,只是第一次 
  10.              // 使用时生成实例,提高了效率! 
  11.              if (instance == null) 
  12.              synchronized(lock) { 
  13.                    instance = new Singleton(); 
  14.              } 
  15.              return instance; 
  16.        } 


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

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

Java代码  
北京联动北方科技有限公司

  1. package com.bijian.study; 
  2. public class Singleton { 
  3.       
  4.        private static Singleton instance = null; 
  5.        private static String lock = new String(); 
  6.       
  7.        public static Singleton getInstance() { 
  8.             
  9.              // 这个方法比上面有所改进,不用每次都进行生成对象,只是第一次 
  10.              // 使用时生成实例,提高了效率! 
  11.              if (instance == null) 
  12.              synchronized(lock) { 
  13.                    if (instance == null) { 
  14.                          instance = new Singleton(); 
  15.                    } 
  16.              } 
  17.              return instance; 
  18.        } 


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

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

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

Java代码  
北京联动北方科技有限公司

  1. package com.bijian.study; 
  2. public class Singleton { 
  3.       
  4.        private static Singleton instance = null; 
  5.        private static String lock = new String(); 
  6.       
  7.        public static Singleton getInstance() { 
  8.             
  9.              // 这个方法比上面有所改进,不用每次都进行生成对象,只是第一次 
  10.              // 使用时生成实例,提高了效率! 
  11.              if (instance == null) 
  12.              synchronized(lock) { 
  13.                    if (instance == null) { 
  14.                          Singleton temp = new Singleton(); 
  15.                          instance = temp; 
  16.                    } 
  17.              } 
  18.              return instance; 
  19.        } 




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论