• 实现CAS自旋锁


    CAS

    • 在高并发场景,可以使用加锁、CAS来保证原子性,但是加锁是很重量级的操作,CAS类似于乐观锁
    • CAS ( Compare and swap )比较并交换,是实现并发算法时常用到的技术,包含三个操作数:内存位置、预期原值、更新值
    • 执行CAS操作的时候,将内存位置中的值与预期原值比较
      • 如果匹配,会将该位置的值更新为新值,
      • 如果不匹配就不会做任何操作,或者重试,这种重试被称为自旋,多个线程同时执行CAS操作,只有一个会成功
    • CAS 是JDK提供的非阻塞原子操作,通过硬件保证了比较-更新的原子性
    • CAS 是一种系统原语,原语属于操作系统用于范畴,由若干条指令组成,用于完成某个功能,原语的执行必须是连续的,在执行过程中不允许被中断,所以说CAS是一条CPU的原子指令,不会造成数据不一致的问题
    • JDK提供的CAS机制,在汇编层级会禁止变量两侧的指令优化,然后使用 cmpxchg(比较并交换) 指令比较并更新变量值
    • 执行 cmpxchg 指令的时候,会判断当前系统是否为多核系统,
      • 如果是就给总线加锁,只有一个线程可以对总线加锁成功,加锁成功后执行CAS操作
      • 所以CAS的原子性实际上是CPU实现独占的,比起synchronized,CAS的排他时间要短很多,多线程情况下性能会更好

    CAS自旋锁

    • CAS利用CPU的指令保证了操作的原子性,达到锁的效果
    • 自旋锁也就是获取锁失败的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,直到成功获取锁,或者超时,放在CAS就是执行一个CAS操作,不断的去执行CAS操作,直到CAS操作被成功执行
    • 这样的好处是减少了线程上下文的切换,缺点是循环会消耗CPU

    示例:不通过 synchronized 和 lock ,就实现了锁的功能

    public class Caslock {
        
        //是否加锁,初始值为 false,也就是未加锁
        private AtomicBoolean atomicBoolean =new AtomicBoolean(false);
        
        public void lock(){
            System.out.println(Thread.currentThread().getName()+",尝试加锁");
            //原子布尔的值是否是false,是就加锁,把值改为true,不是就释放锁
            while (!atomicBoolean.compareAndSet(false,true)){
                //不是false,加锁失败,由其他线程先加了锁,这里就需要等待
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(Thread.currentThread().getName()+",加锁成功");
        }
        public void unLock(){
            //解锁,把值设为 false
            atomicBoolean.compareAndSet(true,false);
            System.out.println(Thread.currentThread().getName()+",释放锁");
        }
    }
    
    
        private static void testCasLock() throws Exception{
            Caslock caslock = new Caslock();
            new Thread(()->{
                caslock.lock();
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                caslock.unLock();
            },"线程A").start();
    
            Thread.sleep(500);
            new Thread(()->{
                caslock.lock();
                caslock.unLock();
            },"线程B").start();
        }
    
    -- 执行结果是:
        
    线程A,尝试加锁
    线程A,加锁成功
    线程B,尝试加锁
    线程A,释放锁
    线程B,加锁成功
    线程B,释放锁    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
  • 相关阅读:
    CorelDRAWX4的C++插件开发(四十二)纯C++插件开发(6)其它invoke的DISPID的功能如打印时鼠标点击时等等
    零基础该如何学习Java,学习java完整学习路线
    【C语言】每日一题(添加逗号)
    Ajax学习:如何在Chrome网络控制台查看通信报文(请求报文/响应报文)
    蓝牙资讯|Q2中国蓝牙耳机市场发布,搭载苹果Find My的蓝牙耳机正逐步推出
    C 语言指针与函数
    C++实现WebSocket通信(服务端和客户端)
    java学习第二天笔记-java基础概念11-键盘输入-33
    307 周赛t4/85 双周赛t4复盘 6155/6159
    [Python编程:从入门到实践] 变量&字符串
  • 原文地址:https://blog.csdn.net/persistence_PSH/article/details/134520779