zoukankan      html  css  js  c++  java
  • java的无锁原子类

     
     
     
    举个栗子:
    原子性问题我们前面一直都是采用的互斥锁方案。 其实对于简单的原子性问题,还有一种无锁方案。Java SDK 并发包将这种无锁方案封装提炼之后,实现了一系列的原子类。
     
    下面 Test中add的方法在,在多线程的情况下count最终可能<100000;因为 add 方法是非线程安全的。
    1 public class myTest {
    2     long count = 0;
    3     void add(){
    4     int  idx =  0;
    5     while(idx++  < 100000)    {
    6            count += 1;
    7         }
    8 } 
    }
    在下面的代码中,我们将原来的 long 型变量 count 替换为了原子类 AtomicLong,原来的 count+=1替换成了 count.getAndIncrement(),仅需要这两处简单的改动就能使add() 方法变成线程安全的,原子类的使用还是挺简单的。
    1 public class myTest {
    2     AtomicLong count = new AtomicLong(0);
    3     void add(){
    4         int  idx =  0;
    5         while(idx++  < 100000) {
    6             count.getAndIncrement();
    7             }
    8     }
    9 }
    As you know(AYouK), 互斥锁方案为了保证互斥性,需要频繁的执行加锁、解锁操作,而加锁、解锁操作本身就消耗性能;若拿不到锁的线程还会进入阻塞状态,进而触发线程切换,线程切换对性能的消耗也很大。 相比之下,无锁方案则完全没有加锁、解锁的性能消耗,同时还能保证互斥性,既解决了问题,又没有带来新的问题,可谓绝佳方案。可见它最大的好处就是那它是如何做到的呢?  
     
    不得不说的CAS(Compare And Swap,即“比较并交换”,自旋锁等等
    很多编程语言或者系统实现上都大量的使用了CAS。其实原子类性能高的秘密很简单,硬件支持而已。CPU 为了解决并发问题,提供了 CAS 指令。CAS 指令包含 3 个参数:共享变量的内存地址 A、用于比较的值 B 和共享变量的新值 C;并且只有当内存中地址 A 处的值等于 B时,才能将内存中地址 A 处的值更新为新值 C。
     
    下面截取了GetAndIncrement的部分源码,调用了unsafe的 getAndAddLong方法。
     

     

    Unsafe提供了三个方法用于CAS操作,分别是

    1 public final native boolean compareAndSwapObject(Object value, long valueOffset, Object expect, Object update);
    2 
    3 public final native boolean compareAndSwapInt(Object value, long valueOffset, int expect, int update);
    4 
    5 public final native boolean compareAndSwapLong(Object value, long valueOffset, long expect, long update);
    • value 表示 需要操作的对象
    • valueOffset 表示 对象(value)的地址的偏移量(通过Unsafe.objectFieldOffset(Field valueField)获取)
    • expect 表示更新时value的期待值
    • update 表示将要更新的值
    具体过程为每次在执行CAS操作时,线程会根据valueOffset去内存中获取当前值去跟expect的值做对比如果一致则修改并返回true,如果不一致说明有别的线程也在修改此对象的值,则返回false
    总结
    无锁方案相对于互斥锁方案,优点非常多,首先性能好,其次是基本不会出现死锁问题(但可能出现饥饿和活锁问题,因为自旋会反复重试)。Java 提供的原子类大部分都实现了compareAndSet() 方法,基于 compareAndSet() 方法,你可以构建自己的无锁数据结构,但是建议你不要这样做,因为无锁算法没你想象的那么简单。Java 提供的原子类能够解决一些简单的原子性问题,但你可能会发现,上面我们所有原子类的方法都是针对一个共享变量的,如果你需要解决多个变量的原子性问题,建议还是使用互斥锁方案。
     
    原子类虽好,但使用要慎之又慎。
     
    public static void main(String[] args) {
    long count = 0;
    int idx = 0;
    while(idx++ < 10000) {
    count += 1;
    }
    System.out.println(count);

    }
    ==========================================================================           如果您觉得这篇文章对你有帮助,可以【关注我】或者【点赞】,希望我们一起在架构的路上,并肩齐行
    ==========================================================================
  • 相关阅读:
    el-select下拉框选项太多导致卡顿,使用下拉框分页来解决
    vue+elementui前端添加数字千位分割
    Failed to check/redeclare auto-delete queue(s)
    周末啦,做几道面试题放松放松吧!
    idea快捷键
    解决flink运行过程中报错Could not allocate enough slots within timeout of 300000 ms to run the job. Please make sure that the cluster has enough resources.
    用.net平台实现websocket server
    MQTT实战3
    Oracle 查看当前用户下库里所有的表、存储过程、触发器、视图
    idea从svn拉取项目不识别svn
  • 原文地址:https://www.cnblogs.com/amberJava/p/12390917.html
Copyright © 2011-2022 走看看