码迷,mamicode.com
首页 > 其他好文 > 详细

原子性

时间:2019-04-27 21:06:43      阅读:174      评论:0      收藏:0      [点我收藏+]

标签:atomic   ati   oid   相同   如何   image   cat   atom   run   

    我们先聊聊一个经典面试题:i=i++; 我们知道在虚拟机底层它实际上做了三步:

    int temp =i; i = i + 1; i = temp;

    i++实际上的操作分为三个部分:读、改、写

    我们看看下面的例子:

  1. public class TestAtomicDemo {  
  2.     public static void main(String[] args) {  
  3.         AtomicDemo ad = new AtomicDemo();  
  4.             
  5.         for(int i = 0;i<10;i++) {  
  6.             new Thread(ad).start();  
  7.         }  
  8.     }  
  9. }  
  10.     
  11. class AtomicDemo implements Runnable{  
  12.     private int serialNumber = 0;  
  13.         
  14.     @Override  
  15.     public void run() {  
  16.         // TODO Auto-generated method stub  
  17.         try {  
  18.             Thread.sleep(200);  
  19.         }catch(InterruptedException e) {  
  20.                 
  21.         }  
  22.             
  23.         System.out.println(Thread.currentThread().getName()+":"+getSerialNumber() );  
  24.     }  
  25.     
  26.     public int getSerialNumber() {  
  27.         return serialNumber++;  
  28.     }  
  29.     
  30.     public void setSerialNumber(int serialNumber) {  
  31.         this.serialNumber = serialNumber;  
  32.     }  
  33.         
  34.         
  35. }  

技术图片

 

    这里很明显,两个数据相同了,当然这不是每次都会发生的。为什么会发生这种情况?

技术图片

 

    为了方便我们将变量名写成Num,就只画四个线程,根据我们之前说的,num先被线程1读取,然后执行改、写操作,在线程1还没来得及将值写回主存时,线程2读取了主存中的num,此时num依然等于0,这就发生了上面很尴尬的场景了。

    我们可以用上一讲谈到的volatile解决吗?当然不行,volatile解决的是可见性,然而我们这里有两步操作,即使是直接在主存操作,依旧可能出现上述情况,即线程1刚刚把num加完正打算写之前,线程2读取了当前num的值,最后依然出现两个1,因为i++存在读改写三步操作,本来这些操作不可分割,但是现在分割执行,破坏了原子性,我们现在需要用到原子变量。

    JDK1.5之后,java.util.concurrent.atomic为我们提供了大量的原子变量,这些变量有以下几个特点:

  1. 含有volatile的特性,原子变量封装的值都是volatile,保证内存的可见性,随便开个源码都可看到:

    技术图片

     

  2. CAS(Compare-And-Swap)算法保证数据原子可见性

    CAS算法是硬件对并发操作共享数据的支持

    CAS包含了三个操作数:

    内存值V 预估值A 更新值B

    当且仅当V==A时,V=B,否则将不会做任何操作

    技术图片

 

    那么CAS如何解决原子性问题呢?

    当线程1改完值,将主存中的num值修改为1,此时线程2需要进行判断,发现原值与现在值不同,所以什么都不做。这样只会使一个线程修改num,更重要的是CAS算法比普通的同步锁效率高很多,当CAS判定失败,它会放弃对CPU的占用,然后再进行判断。缺点就是我们要手工写一些代码,比如重新判断。

 

    扯了这么多,我们回到原先的题目,使用AtomicInteger来处理原问题的原子性,

技术图片

 

  1. import java.util.concurrent.atomic.AtomicInteger;  
  2.     
  3. public class TestAtomicDemo {  
  4.     public static void main(String[] args) {  
  5.         AtomicDemo ad = new AtomicDemo();  
  6.             
  7.         for(int i = 0;i<10;i++) {  
  8.             new Thread(ad).start();  
  9.         }  
  10.     }  
  11. }  
  12.     
  13. class AtomicDemo implements Runnable{  
  14.     private AtomicInteger serialNumber = new AtomicInteger(0);  
  15.         
  16.     @Override  
  17.     public void run() {  
  18.         // TODO Auto-generated method stub  
  19.         try {  
  20.             Thread.sleep(200);  
  21.         }catch(InterruptedException e) {  
  22.                 
  23.         }  
  24.             
  25.         System.out.println(Thread.currentThread().getName()+":"+getSerialNumber() );  
  26.     }  
  27.     
  28.     public int getSerialNumber() {  
  29.         return serialNumber.getAndIncrement();  
  30.     }     
  31. }  

原子性

标签:atomic   ati   oid   相同   如何   image   cat   atom   run   

原文地址:https://www.cnblogs.com/figsprite/p/10780233.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!