JUC之CAS和原子操作类

1 CAS

1.1 是什么

在这里插入图片描述
在这里插入图片描述

1.2 原理

在这里插入图片描述
在这里插入图片描述

1.3 UnSafe

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.4 自旋锁

在这里插入图片描述

1.5 CAS的问题

1.5.1 死循环消耗CPU资源

1.5.2 ABA问题

在这里插入图片描述
很形象的两个例子:

  1. 隔壁老王动了你GF,但是又回复了原样。你能认为这件事情没有发生过么?
  2. 挪用公款然后抹平了亏空。

【问题解决】
版本号和戳记流水机制。

2 原子操作类

2.1 引用类型原子类

2.1.1 AtomicReference

AtomicReference<Phone> atomicPhone = new AtomicReference<>();

2.1.2 AtomicStampedReference

在这里插入图片描述

2.1.3 AtomicMarkableReference

在这里插入图片描述

2.2 基本类型原子类

通过一个demo来了解AtomicInteger的使用。

package org.example.atomicnum;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

class Number {
    
    
    AtomicInteger number = new AtomicInteger();

    public void increment() {
    
    
        number.getAndIncrement();
    }
}

public class Demo {
    
    
    private static int SIZE = 50;

    private static Number myNumber = new Number();

    private static CountDownLatch countDownLatch = new CountDownLatch(SIZE);

    public static void main(String[] args) {
    
    
        for (int i = 0; i < SIZE; i++) {
    
    
            new Thread(new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    try {
    
    
                        for (int j = 1; j <= 1000; j++) {
    
    
                            myNumber.increment();
                        }
                    } finally {
    
    
                        countDownLatch.countDown();
                    }
                }
            }, String.valueOf(i)).start();
        }

        try {
    
    
            countDownLatch.await();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }

		// 如果没有使用CountDownLatch,很可能main thread查早了,查出来的结果不对。 
        System.out.println("main thread: the result = " + myNumber.number.get());
    }
}

2.3 数组类型原子类

2.4 对象的属性修改原子类

2.4.1 使用目的

以一种线程安全的方式操作非线程安全对象内的某些字段。

2.4.2 使用要求

  1. 更新的对象属性必须使用public volatile进行修饰
  2. 因为属性修改原子类都是抽象类,所以必须使用静态方法newUpdater()来创建更新器实例,并且设置想要更新的类和属性。

2.4.3 AtomicIntegerFieldUpdater使用Demo

package org.example.atomicfieldupdater;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class BankAccount {
    
    
    String bankName = "CCB";

    public volatile int money = 0;

    private static AtomicIntegerFieldUpdater moneyUpdater =
        AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, "money");

    /**
     * 局部微创小手术
     *
     * @param bankAccount
     */
    public static void transfer(BankAccount bankAccount) {
    
    
        moneyUpdater.getAndIncrement(bankAccount);
    }
}

package org.example.atomicfieldupdater;

import java.util.concurrent.CountDownLatch;

public class Demo1 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        CountDownLatch countDownLatch = new CountDownLatch(10);
        BankAccount bankAccount = new BankAccount();

        for (int i = 0; i < 10; i++) {
    
    
            new Thread(new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    try {
    
    
                        for (int j = 0; j < 1000; j++) {
    
    
                            BankAccount.transfer(bankAccount);
                        }
                    } finally {
    
    
                        countDownLatch.countDown();
                    }

                }
            }).start();
        }

        countDownLatch.await();

        System.out.println("money = " + bankAccount.money);
    }
}

2.4.4 AtomicReferenceFieldUpdater使用Demo

package org.example.atomicfieldupdater;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

class MyVar {
    
    
    public volatile Boolean isInit = Boolean.FALSE;

    private static AtomicReferenceFieldUpdater<MyVar, Boolean> initUpdater =
        AtomicReferenceFieldUpdater.newUpdater(MyVar.class, Boolean.class, "isInit");

    public static void init(MyVar myVar) {
    
    
        if (initUpdater.compareAndSet(myVar, Boolean.FALSE, Boolean.TRUE)) {
    
    
            System.out.println(Thread.currentThread().getName() + "\t" + "start init, need 2 seconds.");
            sleepSeconds(2);
            System.out.println(Thread.currentThread().getName() + "\t" + "init over.");
        } else {
    
    
            System.out.println(Thread.currentThread().getName() + "\t" + "Other thread executing init work.");
        }
    }

    private static void sleepSeconds(int seconds) {
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}

public class Demo2 {
    
    
    public static void main(String[] args) {
    
    
        MyVar myVar = new MyVar();

        for (int i = 1; i <= 5; i++) {
    
    
            new Thread(() -> {
    
    
                MyVar.init(myVar);
            }, String.valueOf(i)).start();
        }
    }
}

2.5 原子操作增强类

2.5.1 LongAdder

【速度快的原因】
化整为零,分散热点。
在这里插入图片描述
在这里插入图片描述
【Striped64】
在这里插入图片描述
在这里插入图片描述
【源码分析】
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
case3是一个兜底机制:
在这里插入图片描述
case1
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
【AtomicLong和LongAdder的使用场景】
在这里插入图片描述

参考资料

[1] https://www.bilibili.com/video/BV1ar4y1x727?p=90&spm_id_from=pageDriver&vd_source=f4dcb991bbc4da0932ef216329aefb60

猜你喜欢

转载自blog.csdn.net/kaikai_sk/article/details/131424721