java--无锁并发cas

cas操作流程

public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        Account account = new Account(100);
        Account2 account2 = new Account2(100);
        ArrayList<Thread> threads = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            threads.add(new Thread(() -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(new Random(1).nextInt(10));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                account.get(10);
                account2.get(10);
            }));
        }
        for (Thread thread : threads) {
            thread.start();
        }
        for (Thread thread : threads) {
            thread.join();
        }
        System.out.println("cas :" + account.getBalance());
        System.out.println("无锁 无cas:" + account2.getBalance());
    }
}


class Account {
    private AtomicInteger balance;

    Account(int over) {
        this.balance = new AtomicInteger(over);
    }

    public void get(int num) {
        while (true) {
            int o = balance.get();
            int remain = o - num;
            if (balance.compareAndSet(o, remain)) {
                break;
            }
        }
    }

    int getBalance() {
        return balance.get();
    }
}

class Account2 {
    private int balance;

    Account2(int over) {
        this.balance = over;
    }

    public void get(int num) {
        this.balance -= num;
    }

    int getBalance() {
        return this.balance;
    }
}

典型的银行取款案例运行结果
在这里插入图片描述
发现没有用synchronized同样也保证了线程安全。
在这里插入图片描述
其中的关键是这个compareAndSet比较并设置值,简称CAS,它是原子操作的,传入目标值和想要改为多少的值,会先拿到目标值查看该值是不是还是传入的值,如果还是说明没有动过,然后重新设置新值返回true,如果被其他线程动过了,返回false进入下一次循环
在这里插入图片描述

Atomic包

Atomic包下提供了很多实现cas操作的工具类

AtomicInteger

 AtomicInteger atomicInteger = new AtomicInteger(10);
        atomicInteger.get(); // 获取值
        atomicInteger.getAndIncrement(); // num++
        atomicInteger.incrementAndGet(); // ++num
        atomicInteger.getAndDecrement(); // num--
        atomicInteger.decrementAndGet(); // --num
        atomicInteger.addAndGet(5); // 添加5然后返回
        atomicInteger.getAndAdd(5); // 返回然后添加5
        atomicInteger.getAndSet(5); // 获取值后重新设置新值5
        atomicInteger.set(5); // 返回然后添加5 // 设置新值5

getAndUpdate

在这里插入图片描述
发现getAndUpdate方法需要传入一个IntUnaryOperator接口
在这里插入图片描述
@FunctionalInterface修饰说明可以用lambda表达式
在这里插入图片描述
在getAndUpdate方法里默认调用的就是applyAsInt方法,也就是说我们可以自己来实现里面的操作只要接受一个参数并返回一个参数即可,内部操作自己实现
在这里插入图片描述
其内部也是cas操作保证线程安全
在这里插入图片描述
AtomicLong ,AtomicBoolean,AtomicReference。。。功能类似

ABA问题

@Slf4j
public class Test2 {
    static AtomicReference<String> arf = new AtomicReference<>("A");

    public static void main(String[] args) throws InterruptedException {
        log.debug("main start...");
        String prev = arf.get();
        other();
        TimeUnit.SECONDS.sleep(1);
        log.debug("change A->C {}", arf.compareAndSet(prev, "C"));
    }

    private static void other() throws InterruptedException {
        new Thread(() -> {
            log.debug("change A->B {}", arf.compareAndSet(arf.get(), "B"));
        }, "t2").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
            log.debug("change B-> A {}", arf.compareAndSet(arf.get(), "A"));
        }, "t3").start();
    }
}

在这里插入图片描述
发现全部操作都成功了,在主线程获取到A的时候然后去等待,线程2和线程3中间多次操作又变回会了A
在这里插入图片描述
当它去比较还是之前拿到的那个值是不是一样的时候,发现是一样的于是操作成功了。

AtomicStampedReference解决ABA问题

原理就是在加一个版本号,每次操作的时候把版本号一并跟新

@Slf4j
public class Test2 {
    static AtomicStampedReference<String> arf = new AtomicStampedReference<>("A", 1);

    public static void main(String[] args) throws InterruptedException {
        log.debug("main start...");
        String prev = arf.getReference();
        int stamp = arf.getStamp();

        other();
        TimeUnit.SECONDS.sleep(1);
        log.debug("change A->C {}", arf.compareAndSet(prev, "C", stamp, stamp + 1));
    }

    private static void other() throws InterruptedException {
        new Thread(() -> {
            String prev = arf.getReference();
            int stamp = arf.getStamp();
            log.debug("change A->B {}", arf.compareAndSet(prev, "C", stamp, stamp + 1));
        }, "t2").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
            String prev = arf.getReference();
            int stamp = arf.getStamp();
            log.debug("change B-> A {}", arf.compareAndSet(prev, "C", stamp, stamp + 1));
        }, "t3").start();
    }
}

在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值