CAS无锁并发

news/2024/5/20 6:51:01 标签: java, juc

无锁实现线程安全

java">public final class Singleton{
    public static void main(String[] args) {
        Account.demo(new AccountCas(10000));
    }
}
class AccountCas implements Account{
    private AtomicInteger balance;
    public AccountCas(int balance){
        this.balance = new AtomicInteger(balance);
    }

    public Integer getBalance(){
        return balance.get();
    }
    public void withdraw(Integer amount){
        while(true){
            // 获取余额的最新值
            int prev = balance.get();
            // 要修改的余额
            int next = prev - amount;
            // 修改(compareAndSet是原子的)
            if(balance.compareAndSet(prev,next)){// 将prev和balance对象中的进行比较,不一样,就修改失败
                break;
            }
        }
    }
}
interface Account {
    Integer getBalance();
    void withdraw(Integer amount);
    /**
     * 启动 1000 个线程, 每个线程做 -10 元 的操作
     * 如果初始余额为 10000 那么正确的结果应当是 0
     */
    static void demo(Account account) {
        List<Thread> ts = new ArrayList<>();
        long start = System.nanoTime();
        for (int i = 0; i < 1000; i++) {
            ts.add(new Thread(() -> {
                account.withdraw(10);
            }));
        }
        ts.forEach(Thread::start);
        ts.forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        long end = System.nanoTime();
        System.out.println(account.getBalance()
                + " cost: " + (end-start)/1000_000 + " ms");
    }
}

CAS的底层是带lock的cmpxchg指令(X86架构),在单核和多核下都能保证[比较-交换]的原子性。
在多核状态下,某个核执行到带lock的指令时,CPU会让总线锁住,当这个核把此指令执行完毕,再开启总线。
这个过程中不会被线程的调度机制打断,保证了多个线程对内存操作的准确性,是原子的。

CAS操作需要volatile的支持
每次CAS时,需要获取value的最新值,和prev比较,二者一样才能修改成功

java">public class AtomicInteger extends Number implements Serializable {
    private static final long serialVersionUID = 6214790243416807050L;
    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
    private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");

    private volatile int value; // 保证该变量的可见性

cas的效率比synchronized高
无锁情况下,即使交换失败,线程也在高速运行
synchronized会让没获得锁的线程阻塞,发生上下文切换

结合CAS操作和volatile,就可以实现无锁并发
适用线程数较少,多核CPU的情况
CAS是基于乐观锁的思想,不怕别的线程来修改共享变量,其他线程改了,就重试
CAS体现的是无锁并发,无阻塞并发

一些使用了CAS方式实现的工具类

原子整数

java">// AtomicInteger 
// AtomicBoolean
// AtomicLong
相关方法是原子的,线程安全,都是使用的CAS
updateAndGet
getAndAdd
incrementAndGet
// 等

原子引用
保护多线程对一个对象引用进行修改时的线程安全问题

java">// AtomicReference<V>
主线程仅能判断出共享变量的值是否与最初值相同,不能判断是否被别的线程修改过
其他线程将变量修改由从A->B->A,主线程进行CAS时是不能感知到的
如果主线程希望:只要有其他线程动过了共享变量,自己的cas就失败
这时,仅比较值是不够的,需要再加一个版本号
// AtomicMarkableReference<V> AtomicStampedReference简化版,只关心是否更改过
// AtomicStampedReference<V> 维护一个版本号,追踪原子引用的变化过程

原子数组
保护数组里的元素

java">// AtomicIntegerArray
// AtomicLongArray
// AtomicReferenceArray<V>

字段更新器
保护某个对象里的属性,成员变量的线程安全性
被保护的变量需要用volatile修饰

java">// AtomicIntegerFieldUpdater
// AtomicLongFieldUpdater
// AtomicReferenceFieldUpdater
public final class Singleton{
    public static void main(String[] args) {
        Student stu = new Student();
        AtomicReferenceFieldUpdater<Student, String> updater =
                AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");
        System.out.println(updater.compareAndSet(stu,null,"张三"));
        System.out.println(stu);
    }
}
@ToString
class Student{
    volatile String name;
}

原子累加器

java">LongAdder // 比AtomicLong性能好很多
// 线程有竞争时,设置多个累加单元Cell,不同线程向多个累加单元上进行累加,最后将结果汇总
// 减少了CAS失败重试
// 源码:(add,longAccumulate,sum)方法 -> 详见满一航老师JUC视频

在这里插入图片描述

Unsafe类

提供了非常底层的,操作内存,线程的方法

java">public final class Singleton{
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        Unsafe unsafe = (Unsafe) theUnsafe.get(null); // 因为该成员变量是static的,所以不需要传对象,传null就好

        // 获取字段的偏移量
        long idOffset = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("id"));
        long nameOffset = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("name"));
        Teacher t = new Teacher();
        unsafe.compareAndSwapInt(t,idOffset,0,1);
        unsafe.compareAndSwapObject(t,nameOffset,null,"张三");

        System.out.println(t);
    }
}
class Teacher{
    volatile int id;
    volatile String name;
    @Override
    public String toString() {
        return "Teacher{" + "id=" + id + ", name='" + name + '\'' + '}';
    }
}

Unsafe类


http://www.niftyadmin.cn/n/176628.html

相关文章

数位板光标抖动 - 解决方案

数位板光标抖动 - 解决方案前言解决方案方案1&#xff1a;强磁干扰方案2&#xff1a;笔尖距离方案3&#xff1a;数位板驱动方案4&#xff1a;关闭Win Ink方案5&#xff1a;显卡设置方案6&#xff1a;第三方软件设置前言 在使用数位板的过程中&#xff0c;经常会出现数位板光标…

力扣431.等差数列划分 差分法无需DP击败100%

题目 如果一个数列 至少有三个元素 &#xff0c;并且任意两个相邻元素之差相同&#xff0c;则称该数列为等差数列。 例如&#xff0c;[1,3,5,7,9]、[7,7,7,7] 和 [3,-1,-5,-9] 都是等差数列。 给你一个整数数组 nums &#xff0c;返回数组 nums 中所有为等差数组的 子数组 个数…

常用的JavaScript 知识点总结-数组

常用的数组方法 文章目录Array.from返回一个真实数组1返回一个真实数组2Array.ofcopyWithinArray.findArray.findIndexArray.fillArray.mapArray.forEachArray.filterArray.pushArr.unshiftArray.popArray.shiftArray.someArray.everyArray.reduceRightArray.reduce累加replace…

express

一. express 简介express 是一个基于 Node.js 平台的极简、灵活的 WEB 应用开发框架&#xff0c;官方网址&#xff1a;https://www.expressjs.com.cn/简单来说&#xff0c;express 是一个封装好的工具包&#xff0c;封装了很多功能&#xff0c;便于我们开发 WEB 应用&#xff0…

Linux上搭建Socks5服务器

说起socks5这玩意是有点久远了。最知名的就属ss5&#xff0c;但这老掉牙的玩意着实不太行。看这个文档&#xff1a;https://blog.csdn.net/qq_24487005/article/details/124602764又要装一大堆工具&#xff0c;还很难配置。曾经我装了这个玩意&#xff0c;想成功访问还要配置半…

BI、大数据、数据中台三者关系,搞不懂的看看这篇文章

大数据、数据中台都是商业智能BI发展到一定阶段的产物&#xff0c;核心都是围绕数据&#xff0c;数据采集、数据处理能力、算力的提升催生了大数据&#xff0c;数据资产和数据服务催生了数据中台&#xff0c;核心的数仓建模自商业智能BI一脉相承未曾改变&#xff0c;最终出口还…

Golang 中 defer 理解

在Go 语言中&#xff0c;defer 关键字用于在函数返回前执行一些代码。而 ants.Release() 是 Go 语言中一个用于释放资源的函数。具体来说&#xff0c;ants.Release() 是一个来自第三方包 Ants 的函数&#xff0c;Ants 是一个用于实现 goroutine 池的包&#xff0c;用于管理和复…

电容在微分、积分电路中的本质以及应用

很多朋友觉得PID是遥不可及&#xff0c;很神秘&#xff0c;很高大上的一种控制&#xff0c;对其控制原理也很模糊&#xff0c;只知晓概念性的层面&#xff0c;知其然不知其所以然&#xff0c;那么本期从另类视角来探究微分、积分电路的本质&#xff0c;意在帮助理解PID的控制原…