CAS无锁并发

文章介绍了Java中如何使用CAS操作和Atomic类实现线程安全的无锁编程。通过AccountCas类的示例展示了AtomicInteger在并发修改余额时的用法,解释了CAS的原子性和volatile关键字的作用。同时提到了Unsafe类在底层操作中的应用,以及相关原子类如AtomicReference、AtomicIntegerArray等,用于保证多线程环境下的数据一致性。
摘要由CSDN通过智能技术生成

无锁实现线程安全

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

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

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方式实现的工具类

原子整数

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

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

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

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

// AtomicIntegerArray
// AtomicLongArray
// AtomicReferenceArray<V>

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

// 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;
}

原子累加器

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

在这里插入图片描述

Unsafe类

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

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类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值