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

原子操作不一定都是线程安全的

阅读更多

Java中原子操作是线程安全的论调经常被提到。根据定义,原子操作是不会被打断地的操作,因此被认为是线程安全的。实际上有一些原子操作不一定是线程安全的。
  
  这个问题出现的原因是尽量减少在代码中同步要害字。同步会损害性能,虽然这个损失因JVM不同而不同。另外,在现代的 JVM中,同步的性能正在逐步提高。尽管如此,使用同步仍然是有性能代价的,并且程序员永远会尽力提高他们的代码的效率,因此这个问题就延续了下来。
  
  在java中,32位或者更少位数的赋值是原子的。在一个32位的硬件平台上,除了double和long型的其它原始类型通常都是使用 32位进行表示,而double和long通常使用64位表示。另外,对象引用使用本机指针实现,通常也是32位的。对这些32位的类型的操作是原子的。
  
  这些原始类型通常使用32位或者64位表示,这又引入了另一个小小的神话:原始类型的大小是由语言保证的。这是不对的。java语言保证的是原始类型的表数范围而非JVM中的存储大小。因此,int型总是有相同的表数范围。在一个JVM上可能使用32位实现,而在另一个JVM上可能是64位的。在此再次强调:在所有平台上被保证的是表数范围,32位以及更小的值的操作是原子的。
  
  那么,原子操作在什么情况下不是线程安全的?主要的一点是他们也许确实是线程安全的,但是这没有被保证!java线程答应线程在自己的内存区保存变量的副本。答应线程使用本地的私有拷贝进行工作而非每次都使用主存的值是为了提高性能。考虑下面的类:
  
  
  class RealTimeClock
  {
   private int clkID;
   public int clockID()
   {
    return clkID;
   }
   public void setClockID(int id)
   {
   clkID = id;
   }
  //...
  }
  
  现在考虑RealTimeClock的一个实例以及两个线程同时调用setClockID和clockID,并发生以下的事件序列:
  
  T1 调用setClockID(5)
  T1将5放入自己的私有工作内存
  T2调用setClockID(10)
  T2将10 放入自己的私有工作内存
  T1调用clockID,它返回5
  5是从T1的私有工作内存返回的
  
  对 clockI的调用应该返回10,因为这是被T2设置的,然而返回的是5,因为读写操作是对私有工作内存的而非主存。赋值操作当然是原子的,但是因为 JVM答应这种行为,因此线程安全不是一定的,同时,JVM的这种行为也不是被保证的。
  
  两个线程拥有自己的私有拷贝而不和主存一致。假如这种行为出现,那么私有本机变量和主存一致必须在以下两个条件下:
  
  1、变量使用volatile声明
  2、被访问的变量处于同步方法或者同步块中
  
  假如变量被声明为volatile,在每次访问时都会和主存一致。这个一致性是由java 语言保证的,并且是原子的,即使是64位的值。(注重很多JVM没有正确的实现volatile要害字。你可以在www.javasoft.com找到更多的信息。)另外,假如变量在同步方法或者同步块中被访问,当在方法或者块的入口处获得锁以及方法或者块退出时释放锁是变量被同步。
  使用任何一种方法都可以保证ClockID返回10,也就是正确的值。变量访问的频度不同则你的选择的性能不同。假如你更新很多变量,那么使用volatile可能比使用同步更慢。记住,假如变量被声明为volatile,那么在每次访问时都会和主存一致。与此对照,使用同步时,变量只在获得锁和释放锁的时候和主存一致。但是同步使得代码有较少的并发性。
  
  假如你更新很多变量并且不想有每次访问都和主存进行同步的损失或者你因为其它的原因想排除并发性时可以考虑使用同步。

分享到:
评论

相关推荐

    C#线程锁介绍源码

    互锁操作是原子的,即整个操作是不能由相 同变量上的另一个互锁操作所中断的单元。这在抢先多线程操作系统中是很重要的,在这样的操作系统中,线程可以在从某个内存地址加载值之后但是在有机会更改 和存储该值之前被...

    Linux中的线程局部存储(1)

    那么对于那些系统不支持原子操作的自定义数据类型,在不使用锁的情况下如何做到线程安全呢?本文将从线程局部存储方面,简单讲解处理这一类线程安全问题的方法。  一、数据类型  在C/C++程序中常存在全局变量、...

    探究iOS多线程究竟不安全在哪里?

    当我们讨论property多线程安全的时候,很多人都知道给property加上atomic attribute之后,可以一定程度的保障多线程安全,类似: @property (atomic, strong) NSString* userName; 事情并没有看上去这么简单,要...

    Java并发编程(学习笔记).xmind

    无状态对象一定是线程安全的,大多数Servlet都是无状态的 原子性 一组不可分割的操作 竞态条件 基于一种可能失效的观察结果来做出判断或执行某个计算 复合操作:执行复合操作期间,要持有锁...

    Linux多线程同步方式

    当多个线程共享相同的内存时,需要确保每个线程看到一致的数据视图,当多个线程同时去修改这片内存时,可能出现偏差,得到与预期不符合的值。为啥需要同步,一件事情逻辑上一定是有序的,即使在并发环境下;而操作...

    多线程CPU压力测试源码

    源码调用了精易模块,。...老电脑一定要不要勾选不延迟!。本程序用于测试计算CPU在多线程下同时计算对CPU产生的压力!。没有做防护!如果需要做防炸机在第二个时钟下写代码!。本程序原理十分简单!。@氢原子。

    天若OCR文字识别,按F4截图转文字准确率99%

    天若OCR文字识别,按F4截图转文字准确率99% 天若OCR文字识别,按F4截图转文字准确率99% ...但最小安全性并不保证线程读取到的值,一定是某个线程写完后的值。最小安全性保证线程读取到的值不会无中生

    Java并发编程实践 PDF 高清版

    2.1 什么是线程安全性 2.2 原子性 2.3 锁 2.4 用锁来保护状态 2.5 活跃度与性能 第3章 共享对象 3.1 可见性 3.2 发布和逸出 3.3 线程封闭 3.4 不可变性 3.5 安全发布 . 第4章 组合对象 4.1 设计线程安全的类 4.2 ...

    java8源码-geektime-java-common-mistakes:geektime-java-常见错误

    java8 源码 ...是线程安全的,并不代表一定线程安全 线程安全的并发容器只能保证方法级的操作原子性,多个操作间没有安全性保证 ConcurrentHashMap 的能力有限: 使用了 ConcurrentHashMap,不代表

    Java 7并发编程实战手册

    6.4 使用按优先级排序的阻塞式线程安全列表 215 6.5 使用带有延迟元素的线程安全列表 221 6.6 使用线程安全可遍历映射 226 6.7 生成并发随机数 231 6.8 使用原子变量 233 6.9 使用原子数组 237 第...

    jstack生成的Thread Dump日志.docx

    临界区的设置是为了保证其内部的代码执行的原子性和完整性,但因为临界区在任何时间只允许线程串行通过,这和我们使用多线程的初衷是相反的。如果在多线程程序中大量使用synchronized,或者不适当的使用它,会造成...

    java面试题

    Collections是针对集合类的帮助类,它提供了一系列针对集合的搜索,排序,线程安全化等操作。 final、finally、finalize的区别? 答:final用于声明属性方法和类,分别表示:属性不可变,方法不可覆盖,类不可继承...

    Java并发编程的艺术_非扫描

    第7章介绍了Java中的原子操作类,并给出一些实例。第8章介绍了Java中提供的并发工具类,这是并发编程中的瑞士军刀。第9章介绍了Java中的线程池实现原理和使用建议。第10章介绍了Executor框架的整体结构和成员组件。...

    \java超强笔记(超级经典)

    Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。Callable和Runnable有几点不同: Callable规定的方法是call(),而Runnable规定的方法是run(). ...

    Java 虚拟机面试题全面解析(干货)

    为什么基于 volatile变量的运算在并发下不一定是安全的? 为什么使用 volatile? 并发与线程 并发与线程的关系? 什么是线程? 实现线程有哪些方式? Java线程的实现 Java线程调度 什么是线程调度? 线程调度有哪些方法? ...

    MYSQL实现添加购物车时防止重复添加示例代码

    因为两步不是原子的操作,所以就出现了多线程的安全问题,下面话不多说了,来跟随小编一起来看看详细的解决过程吧,会对你有一定的帮助的。 MySQL insert有一个比较高级的操作 示例代码: INSERT INTO t_xs_shopping...

    java面试800题

    volatile:volatile变量表示保证它必须是与主内存保持一致,它实际是""变量的同步"", 也就是说对于volatile变量的操作是原子型的,如用在long 或 double变量前,一般用于多线程编程。 abstract:抽象,必须重载,修饰...

    从青铜到王者的路线来聊聊Synchronized的底层实现原理

    2.1 多线程一定快吗? 2.2 上下文切换 2.3 测试上下文切换次数 2.4 Java内存模型 2.5 主内存与工作内存之间的数据交互过程 三、秩序白银 3.1 多线程带来的可见性问题 3.2 多线程带来的原子性问题 3.3 多线程带来...

    VC++ COM组件开发相关实例

    这些编程规范定义了组件的操作、接口的 访问等等。 3.2 COM接口 COM接口是组件的核心,从一定程度上 讲"COM接口是组件的一切". COM接口给用户提供了访问组件的方式. 通过COM接口提供的函数,可以使用组件 的...

Global site tag (gtag.js) - Google Analytics