• 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);

    }
    ==========================================================================           如果您觉得这篇文章对你有帮助,可以【关注我】或者【点赞】,希望我们一起在架构的路上,并肩齐行
    ==========================================================================
  • 相关阅读:
    php提取字符串中的数字
    php中urlencode()和urldecode()URL编码函数浅析[转]
    B2C电子商务系统研发——商品SKU分析和设计(一)
    电商ERP系统——商品SKU与库存设计
    爬虫实战:爬虫之 web 自动化终极杀手 ( 上)
    鸟瞰前端 , 再论性能优化
    反-反爬虫:用几行代码写出和人类一样的动态爬虫
    小巧玲珑:机器学习届快刀XGBoost的介绍和使用
    从零开始接入腾讯云智能推荐
    云技术平台赋能媒体融合发展创新
  • 原文地址:https://www.cnblogs.com/amberJava/p/12390917.html
Copyright © 2020-2023  润新知