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

饿汉式:

  1. 1 package com.bijian.study;
  2. 2
  3. 3 public class Singleton {
  4. 4
  5. 5 private static Singleton instance = new Singleton();
  6. 6
  7. 7 public static synchronized Singleton getInstance() {
  8.  
  9. 13 return instance;
  10. 14 }
  11. 15 }

懒汉式:

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

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

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

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

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

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

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

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

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

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

单例模式中的多线程分析synchronized的更多相关文章

  1. 线程的同步之Synchronized在单例模式中的应用

    synchronized在单例模式中的使用 在单例模式中有一种懒汉式的单例,就是类初始化的时候不创建对象.等第一次获取的时候再创建对象.这种单例在单线程下是没有问题的获取的也都是同一个对象.但是如果放 ...

  2. 详细分析 Java 中实现多线程的方法有几种?(从本质上出发)

    详细分析 Java 中实现多线程的方法有几种?(从本质上出发) 正确的说法(从本质上出发) 实现多线程的官方正确方法: 2 种. Oracle 官网的文档说明 方法小结 方法一: 实现 Runnabl ...

  3. Android多线程分析之四:MessageQueue的实现

    Android多线程分析之四:MessageQueue的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处 在前面两篇文章<Androi ...

  4. Android多线程分析之三:Handler,Looper的实现

    Android多线程分析之三:Handler,Looper的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处 在前文<Android多 ...

  5. Android多线程分析之二:Thread的实现

    Android多线程分析之二:Thread的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处   在前文<Android多线程分析之一 ...

  6. Android四个多线程分析:MessageQueue实现

    Android四个多线程分析:MessageQueue的实现 罗朝辉 (http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 在前面两篇文章<Android多线 ...

  7. OS X 和iOS 中的多线程技术(上)

    OS X 和iOS 中的多线程技术(上) 本文梳理了OS X 和iOS 系统中提供的多线程技术.并且对这些技术的使用给出了一些实用的建议. 多线程的目的:通过并发执行提高 CPU 的使用效率,进而提供 ...

  8. Apollo配置中心源码分析

    Apollo配置中心源码分析 1. apollo的核心代码分享 SpringApplication启动的关键步骤 在SpringApplication中,会加载所有实现了Init方法的类 protec ...

  9. java 多线程8 : synchronized锁机制 之 方法锁

    脏读 一个常见的概念.在多线程中,难免会出现在多个线程中对同一个对象的实例变量或者全局静态变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数 ...

随机推荐

  1. Js 冒泡事件阻止

    Js 冒泡事件阻止   1. 事件目标 现在,事件处理程序中的变量event保存着事件对象.而event.target属性保存着发生事件的目标元素.这个属性是DOM API中规定的,但是没有被所有浏览 ...

  2. NodeJS利用mongoose模糊查询MongoDB

    在Node.js中,直接硬编码可以 Posts.where('title',/答案/); 但是 通过 字符串构造 不行 var qs = '/'+req.query.search+'/'; Posts ...

  3. JSP常见指令

    JSP常见指令 标签: jspincludeservletjavaappletarchive 2011-11-07 20:07 13193人阅读 评论(3) 收藏 举报 版权声明:本文为博主原创文章, ...

  4. 对比Linux系统和Windows系统哪个更好

    最近半年来,我一直在读一本叫做<Linux就该这么学>的Linux教材,确实让我进步的很快,也慢慢的让Linux系统走入了我的知识认知中,那么学习前我们来对比下Windows和Linux的 ...

  5. [强连通分量] POJ 2762 Going from u to v or from v to u?

    Going from u to v or from v to u? Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 17089 ...

  6. Jmeter—6 CSV Data Set Config 通过文件导入数据

    线程组循环次数大于1的时候,请求里每次提交的数据都相同.有的系统限制了不能提交相同数据,我们通过 CSV Data Set Config 加载csv文件数据. 1 创建一个文本文件,输入参数值保存为. ...

  7. UIImagePickerController 获取相片视频

    1.UIImagePickerController的静态方法: imagepicker = [[UIImagePickerController alloc]init];    //UIImagePic ...

  8. linux信号处理时机

    信号号称所谓软中断,事实上,还是没有真正的硬件中断那样能随时改变cpu的执行流 硬件中断之所以能一发生就得到处理是因为处理器在每个指令周期的结尾都会去检查中断,这种粒度是很细的 但是信号的实现只是在进 ...

  9. linux上安装php+gd扩展

    515 cd zlib-1.2.3 516 ./configure --prefix=/usr/local/zlib2 517 make && make install 518 cd ...

  10. memcache详解

    MemCache是一个自由.源码开放.高性能.分布式的分布式内存对象缓存系统,用于动态Web应用以减轻数据库的负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高了网站访问的速度. Me ...