关闭

原子性与可见性

标签: java
299人阅读 评论(0) 收藏 举报
分类:

http://www.cnblogs.com/mengyan/archive/2012/08/22/2651575.html


一、定义

1.可见性

在多核处理器中,如果多个线程对一个变量(假设)进行操作,但是这多个线程有可能被分配到多个处理器中运行,那么编译器会对代码进行优化,当线程要处理该变量时,多个处理器会将变量从主存复制一份分别存储在自己的片上存储器中,等到进行完操作后,再赋值回主存。(这样做的好处是提高了运行的速度,因为在处理过程中多个处理器减少了同主存通信的次数);同样在单核处理器中这样由于“备份”造成的问题同样存在!

这样的优化带来的问题之一是变量可见性——如果线程t1与线程t2分别被安排在了不同的处理器上面,那么t1与t2对于变量A的修改时相互不可见,如果t1给A赋值,然后t2又赋新值,那么t2的操作就将t1的操作覆盖掉了,这样会产生不可预料的结果。所以,即使有些操作时原子性的,但是如果不具有可见性,那么多个处理器中备份的存在就会使原子性失去意义。

2.原子性:

众所周知,原子是构成物质的基本单位(当然电子等暂且不论),所以原子的意思代表着——“不可分”;

由不可分性可知,原子性是拒绝多线程操作的(只有分解为多步操作,多个线程才能对其操作:就像一个盒子里有多个兵乓球,多个人能够从盒子里拿乒乓球;如果盒子只有一个兵乓球,一个人拿的话,其他人就拿不到了;这就是原子性,乒乓球就具有原子性,人就相当于原子)

 简而言之——不被线程调度器中断的操作,如:

赋值或者return。比如"a = 1;"和 "return a;"这样的操作都具有原子性

原子性不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作!

3.非原子性操作

类似"a += b"这样的操作不具有原子性,在某些JVM中"a += b"可能要经过这样三个步骤:

(1)取出a和b

(2)计算a+b

(3)将计算结果写入内存

如果有两个线程t1,t2在进行这样的操作。t1在第二步做完之后还没来得及把数据写回内存就被线程调度器中断了,于是t2开始执行,t2执行完毕后t1又把没有完成的第三步做完。这个时候就出现了错误,相当于t2的计算结果被无视掉了。所以上面的买碘片例子在同步add方法之前,实际结果总是小于预期结果的,因为很多操作都被无视掉了。

类似的,像"a++"这样的操作也都不具有原子性。所以在多线程的环境下一定要记得进行同步操作

4.原子性与可见性的关系

原子性与可见性并没有直接关联的关系。说道这里,不得不要讨论一下多线程带来的问题及其本质。

(1)先来点废话,有可能会将多核与单核处理器进行不同的区分,这里我搞混了,其实在代码级别来说它们是相同的!

单核机器的多线程其实是为每个线程分配一个时间片段,所以实际上这些线程在微观来说在一个时间段内只有一个在执行。这里产生的问题是如果一个线程操作一个内存空间然后突然被线程调度器终止掉(挂起),由另一个线程获取CPU时间来对这个空间进行操作,那么着之间会产生不可预知的问题。

多核机器的基本原理与此是相同的,不同的是在同一时间,可能会有多个线程同时在进行操作(因为每个核心都可运行一项操作)。前面讲到,多核机器由于多核的原因其多个线程对于相同内存的操作会产生可见性的问题。(可见性在单核和多核中同样都存在)

(2)多线程中可见性造成的问题:

多个线程对相同变量的修改相互不可见,导致某部分操作被覆盖,比如:

count++; t1与t2两个线程准备操作它,当t1在自己存储空间内修改完count值之后,并没有及时将count修改回去,而是执行了count其它的操作——这时候,t2开始执行该操作,但是它并没有发现count值进行了改变,这样就造成了count值没有被及时更新而产生的相关错误。

(3)其它问题:

同样是count++语句,产生问题的语句还可能是其它原因造成的:t1与t2执行该语句,t1只比t2稍慢一点,t2修改后count,t1又将自己的结果写入count,这样t1的结果会对t2的结果进行覆盖,这种覆盖会造成一项不到的错误。

(1.2)非原子性造成的问题,多个线程在执行动作时某一方的“动作”“覆盖”了另一方;

(5)讨论:

可见性的问题造成了多线程的问题的一部分,确定变量的可见性只能解决一部分多线程的问题;而操作原子性是解决多线程的总的方法,因为它拒绝多个线程在同一时刻操作相同的一段内存。

 

5.volatile与synchronized关键字

(1)volatile

volatile赋予了变量可见——禁止编译器对成员变量进行优化,它修饰的成员变量在每次被线程访问时,都强迫从内存中重读该成员变量的值;而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存,这样在任何时刻两个不同线程总是看到某一成员变量的同一个值,这就是保证了可见性。文摘:

Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量
的原始值对比。这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。而volatile关键字就是提示
VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。
由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。 就跟C中的一样 禁止编译器进行
优化~~~~

注意:

如果给一个变量加上volatile修饰符,就相当于:每一个线程中一旦这个值发生了变化就马上刷新回主存,使得各个线程取出的值相同。编译器不要对这个变量的读、写操作做优化。但是值得注意的是,除了对long和double的简单操作之外,volatile并不能提供原子性。所以,就算你将一个变量修饰为volatile,但是对这个变量的操作并不是原子的,在并发环境下,还是不能避免错误的发生!

参考链接: http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html

(2)synchronized

synchronized为一段操作或内存进行加锁,它具有互斥性。当线程要操作被synchronized修饰的内存或操作时,必须首先获得锁才能进行后续操作;但是在同一时刻只能有一个线程获得相同的一把锁(对象监视器),所以它只允许一个线程进行操作。

简单的理解方法:

synchronized(object) method();

这相当与为menthod()加了一把锁,这把锁就是object对象;当线程要访问method方法时,需要获取钥匙:object的对象监视器,如果该钥匙没人拿走(之前没有线程操作该方法或操作完成),则当前线程拿走钥匙(获取对象监视器),并操作方法;当操作完方法后,将“钥匙”放回原处!

如果“钥匙”不在原处,则该线程需要等待别人把钥匙放回来(等待即进入阻塞状态);如果多个线程要获取该钥匙,则它们需要进行“竞争”(一般是根据线程的优先级进行竞争)

 

 


0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

内存可见性和原子性:Synchronized和Volatile的比较

Java多线程之内存可见性和原子性:Synchronized和Volatile的比较     【尊重原创,转载请注明出处】http://blog.csdn.net/guyuealian/artic...
  • guyuealian
  • guyuealian
  • 2016-09-13 14:58
  • 4340

5、并发编程的3个概念:原子性、可见性、有序性

并发程序正确地执行,必须要保证原子性、可见性以及有序性。只要有一个没有被保证,就有可能会导致程序运行不正确。 原子性:一个操作或多个操作要么全部执行完成且执行过程不被中断,要么就不执行。 可见性:当多...
  • u010796790
  • u010796790
  • 2016-08-08 21:08
  • 1688

原子性与可见性 volatile与synchronized关键字

一、定义 1.可见性 在多核处理器中,如果多个线程对一个变量(假设)进行操作,但是这多个线程有可能被分配到多个处理器中运行,那么编译器会对代码进行优化,当线程要处理该变量时,多个处理器会将变量从主...
  • zmissm
  • zmissm
  • 2014-04-11 23:16
  • 1103

JAVA的原子性和可见性的理解

1、原子性 (1)原子是构成物质的基本单位(当然电子等暂且不论),所以原子的意思代表着——“不可分”; (2)原子性是拒绝多线程操作的,不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来...
  • wohaqiyi
  • wohaqiyi
  • 2017-03-28 14:16
  • 1143

java并发之原子性与可见性(一)

原子性 原子是世界上的最小单位,具有不可分割性。比如 a=0;(a非long和double类型) 这个操作是不可分割的,那么我们说这个操作时原子操作。再比如:a++; 这个操作实际是a = a + 1...
  • maosijunzi
  • maosijunzi
  • 2014-01-15 17:02
  • 23045

Java多线程中提到的原子性和可见性、有序性

原子性 原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行。如果一个操作时原子性的,那么多线程并发的情况下,就不会出现变量被修改的情况比如 a=0...
  • jaryle
  • jaryle
  • 2016-05-18 10:56
  • 5072

线程安全之可见性、有序性以及原子性

可见性:一个线程对主内存的修改可以及时的被其他线程观察到。 有序性:一个线程观察其他线程中的指令执行顺序,由于指令 重排序的存在,该观察结果一般杂乱无序。 原子性:提供了互斥访问。 特性 操作 ...
  • u013855332
  • u013855332
  • 2016-06-25 18:46
  • 1385

java-多线程深入(二)互斥性和可见性

(一)互斥性 互斥性,即原子性。原子,指最小的物质,具体不可再分性。 CPU运算中,对多线程进行时间片分割执行,一个程序块执行时不可分割,即满足互斥性原子性。 java中保证互斥性的方法: 1.用sy...
  • wangpeifeng669
  • wangpeifeng669
  • 2015-01-26 10:12
  • 1488

Java| Java并发特性: 原子性、有序性、可见性

一.原子性是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其它线程干扰。 Java中的原子操作包括: 1)除long和double之外的基本类型的赋值操作 2...
  • u011479200
  • u011479200
  • 2017-03-20 17:04
  • 480

深入理解Java虚拟机笔记---原子性、可见性、有序性

Java内存模型是围绕着并发过程中如何处理原子性、可见性、有序性这三个特征来建立的,下面是这三个特性的实现原理: 1.原子性(Atomicity)    由Java内存模型来直接保证的原子性变量操作包...
  • xtayfjpk
  • xtayfjpk
  • 2014-12-16 22:43
  • 3290
    个人资料
    • 访问:666259次
    • 积分:7030
    • 等级:
    • 排名:第3803名
    • 原创:86篇
    • 转载:280篇
    • 译文:0篇
    • 评论:51条
    博客专栏
    文章分类
    最新评论