zoukankan      html  css  js  c++  java
  • 原子性

        我们先聊聊一个经典面试题: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. }  
  • 相关阅读:
    (selenium+python)_UI自动化02_元素定位
    (selenium+python)_UI自动化01_Mac下selenium环境搭建
    2019_Chrome和ChromeDriver对应关系
    charles_02_模拟弱网测试
    charles_01_打断点修改接口请求&返回数据
    Jmeter和LoadRunner的区别
    Linux 系统结构,nglinx
    jmeter,badboy,jar包文件 常数吞吐量计时器?
    863公司 linux软测题
    linux centOS 7 GUI安装
  • 原文地址:https://www.cnblogs.com/figsprite/p/10780233.html
Copyright © 2011-2022 走看看