`
paddy.w
  • 浏览: 497478 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

同步锁的失败可能

    博客分类:
  • Java
 
阅读更多
以下例子参考http://developer.51cto.com/art/201104/256239.htm

        网上闲逛时发现一篇博文,讲的是单例同步锁时失败的可能,提到的错误自己基本都忽略了,下面以其中的例子说一下自己的理解。

        单例模式是比较简单直接的:
public class Singleton {  

	private static Singleton instance = null;  

	private Singleton(){}  

	public static Singleton  getInstance() {  

		if(instance == null) {  
			instance = new Singleton();  
		}  

		return instance;  
	}  
}

        这也是在初学单例模式时会提到的一个采用延迟加载实现的例子,实际上这个例子并不能“保证”单例,在多线程高并发的情况下,如果瞬间同时初始访问getInstance方法,返回的可能就是不同的实例。于是就有了下面的简单的同步机制:
public class Singleton {  
    
    private static Singleton instance = null;  
 
    private Singleton(){}  
 
    public synchronized static Singleton getInstance() {  
        
       if(instance == null) {  
           instance = new Singleton();  
       }  
 
       return instance;  
    }  
}

        这种方式保证了线程安全,但是我们只需要在生成实例的过程中保证其同步即可,不需要对访问也进行加锁。很明显,简单的在类实例范围内对其加锁,性能是其瓶颈。为了避免这种情况,便会有下面的想法:
public class Singleton {  
    
    private static Singleton instance = null;  
 
    private Singleton(){}  
      
    public static Singleton getInstance() {  
 
       if(instance == null) {  
 
           synchronized(Singleton.class) {  
 
              if(instance == null) {  
 
                  instance = new Singleton();  
 
              }
           }
       }

       return instance;  
    }
}

        其实这样便可以保证单例了,但是却保证不了单例的准确性。也就是说不同的线程可以拿到同一个实例,但是在某些情况下,会取到实例的错误状态。在本例中,初始化Singleton对象和将对象地址赋给instance的顺序是不确定的。也即可能有以下两种情况:
        1、在初始化之前将对象引用赋给instance。这时,instance有了实际的引用,但对象还没有初始化。其他线程此时如果获取到instance,可能就是只有引用而还没有初始化的实例。
        2、在初始化之后将对象引用赋给instance。这是正确的状态,保证了单例也保证了正确性。

        这种不确定性不能“保证”单例的正确性。再看下面的改进方案:
public class Singleton {  
    
    private static Singleton instance = null;  
 
    private Singleton(){}  
 
    public static Singleton getInstance() {  
 
       if(instance == null) {  
 
           Singleton temp;  
 
           synchronized(Singleton.class) {  
 
              temp = instance;  
 
              if(temp == null) {  
 
                  synchronized(Singleton.class) {  
 
                     temp = new Singleton();  
 
                  }  
                  instance = temp;  
              }  
           }  
       }  
 
       return instance;  
    }  
}

        这种方式制造了一个内存屏障,即使用了一个临时变量来保证“初始化操作和赋引用操作”的原子性。这种方式从代码方面看已经没有问题了,但是注意到在同步语句块之外的instance=temp,根据博文的解释:同步语句块内的操作必须在语句块结束之前完成,但是代码中同步块之外的操作有可能被编译器放到块内执行(只是存在这种可能性)。这一点细节确实不容易考虑到,需要了解一些JVM的知识,这方面没什么研究,保留意见。
        看最后一种方式:
public class Singleton {  
    
    private static volatile Singleton instance = null;  
 
    private Singleton(){}  
 
    public static Singleton getInstance() {  
 
       if(instance == null) {  
 
           synchronized(Singleton.class) {  
 
              if(instance == null) {  
 
                  instance = new Singleton();  
 
              }  
           }  
       }  
       return instance;  
    }  
}

        这里要说一下volatile关键字,这个除了在一定程度上确保同步之外,JDK1.5扩充了volatile语义,简单点说就是保证了对象初始化以及赋引用的有序性,在本例来说就是先初始化后赋引用。这样两层机制保证了单例的唯一性和准确性。
分享到:
评论

相关推荐

    【Java】synchronized同步锁详解

    synchronized同步锁(悲观锁)2.1 synchronized 作用范围2.2 synchronized 核心组件2.3 synchronized 实现 1. Java锁的种类 1.1 乐观锁 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低。 每次去拿...

    J.U.C-AQS框架同步组件之StampedLock乐观锁悲观锁

    乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低(不是没有, 所以还要加锁, 区别于不加锁的乐观读),每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有...

    PS3TrophyIsGood:有史以来最好的PS3奖杯编辑器

    同步失败可能 即使如此也要修改,总而言之,风险自负。 请先确定以阅读以上文字后再点击继续阅读。 变更记录: v1.3.7 1. I've added a Resign option, so that you can resign a trophy folder to your specified ...

    小黑屋001

    更新日志 ...22.修改重命名失败时的处理; 23.修改窗口拖动和在屏幕上显示的位置的处理; 24.增加目录提示条、状态提示条的气球提示,并增加右键禁止功能; 25.锁定状态下不允许添加定时解锁; 26.详细记录

    阿里云ons使用

     4)大规模机器的Cache同步  5)MySQL BinLog订阅数据分发 2、ONS应用场景  异步、解耦、最终一致、并行 3、设计假定  1)每台PC机器都可能down机不可服务  2)任意集群都可能处理能力不足  3)最坏情况一定会...

    pub类库

    主要有读写锁类, 线程类, 线程池类, 定时器类, socket1.1的封装类, ini文件类, txt文件类, 可删除内容的文件类, 查找文件类, 调试输出类, 字符串类, 同步的普通队列和优先级队列类, 智能指针和内存自动管理类,...

    Membase1.7.1第二部分(共二部分)

    Membase 是 NoSQL 家族的一个新的重量级的成员。 Membase是开源项目,源代码采用了Apache2.0的使用许可。该项目托管在GitHub.Source ...•通过把数据复制到多个集群单元和支持快速失败转移来提供系统的高可用性。

    redisStudy.zip

    everysec:表示每秒同步一次(默认值,很快,但可能会丢失一秒以内的数据) 以下问题都是基本回答: 4.redis支持事务吗 redis可以说是半支持事务(假事务),提供了一些在一定程度上支持线程安全和事务的命令。...

    4.9.1 绿色注册专业版及单文件.zip

    7、执行各种操作时,在任务栏按钮上同步显示操作进度。 8、支持通过鼠标拖拽加载虚拟硬盘。 9、支持通过鼠标从资源管理器拖拽的方式复制文件到分区。 10、支持通过右键菜单复制磁盘及分区信息中的文字。 11、发现...

    concurrency-in-scala-with-ce:基于cats-effect库的Scala中异步和并发编程概念简介

    具有Cats-Effect的Scala中的并发 本文基于库,介绍了Scala中的并发异步效果主题。 但是,这里介绍的许多概念不仅适用于其他Scala...同步:线程通过成功或失败来完成任务,然后再执行之后的任何任务。 异步:线程启动某

    基于GO语言大型企业级电商秒杀系统实战教程

    秒杀系统特点就是并发量极大,但实际秒杀成功的请求数量确很少,所以如果不在前端拦截可能造成数据库读写锁冲突,甚至导致死锁,最终请求超时,甚至导致系统崩溃充分利用缓存:利用缓存可以极大提高系统读写速度消息...

    分布式协调工具-ZooKeeper实现动态负载均衡

    在这里稍微变化下,就是允许所有请求都能够创建成功,但是得有个创建顺序,于是所有的请求最终在ZK上创建结果的一种可能情况是这样: /currentMaster/{sessionId}-1 ,/currentMaster/{sessionId}-2,/currentMaster/{...

    中文简体压缩软件RAR 6.0

    它仍然可能恢复不在受损部位,而文件结 构破坏的文件。这对于非固实压缩文件通常有用。 当第二阶段完成,重建结构的压缩文件将被保存为 rebuilt.arcname.rar, 'arcname' 的位置是原始压缩文件名。 RAR/...

    jquery电子文档chm

    注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行。 beforeSend (Function) : 发送请求前可修改 XMLHttpRequest 对象的函数,如添加自定义 HTTP 头。XMLHttpRequest 对象是唯一的参数。 Ajax ...

    sesvc.exe 阿萨德

    否则判断桶的第一个位置(有可能是链表、红黑树)的 key 是否为查询的 key,是就直接返回 value。 如果第一个不匹配,则判断它的下一个是红黑树还是链表。 红黑树就按照树的查找方式返回值。 不然就按照链表的方式遍历...

    Tasker Pro 5.7.0.apk

    查询,问题:请参阅菜单/信息/支持in-appIt不可能修复通过Play商店评论报告的问题... ******按类别排序****** [并非所有设备上都可用的所有操作] * ALERT:Flash,通知LED /声音/振动,取消通知,弹出计时/ ...

    java面试题

    例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。 当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望...

    OCPOCA认证考试指南全册:Oracle Database 11g(1Z0-051,1Z0-052,1Z0-053)--详细书签版(第2/2部分)

    8.4.1 共享锁与排他锁 287 8.4.2 排队机制 287 8.4.3 锁定争用 288 8.4.4 死锁 290 8.5 撤销概述 291 8.6 事务与撤销数据 292 8.7 管理撤销 293 8.7.1 与撤销相关的错误条件 294 8.7.2 用于撤销管理与保留...

Global site tag (gtag.js) - Google Analytics