【Java 高并发手记1】基础入门面试讲解-线程安全性-原子性

版权声明:送人玫瑰,手有余香,一分也是爱 https://blog.csdn.net/m0_37156901/article/details/85858676

Fork me on Gitee

线程安全性- 原子性

前言目录概述

Atomic 包相关的技术和原理相当的重要, 一定要好好学习,是线程安全三大特性之一

  • 原子性
  • 有序性
  • 可见性
AtomicXXX: CAS、 Unsafe.compareAndSwapXXX原理
一定要掌握CAS的原理,compareAndSwapXXX原理
AtomicLong 和 LongAdder的使用,对比,优缺点,原理。
AtomicReference 和 AtomicReferenceFieldUpdater的使用,对比,优缺点,原理
AtomicStampReference: 解决CAS的 ABA 问题
AtomicLongArray,
AtomicBoolean

原子性-Atomic 包

AtomicXXX: CAS、 Unsafe.compareAndSwapXXX
AtomicLong、 LongAddr
两步操作实现安全

// private static int count = 0;
private static AtomicInteger count= new AtomicInteger(0);
// count++;
count.incrementAndGet();

incrementAndGet 方法的源码(面试)

incrementAndGet – getAndAddInt

可以看到incrementAndGet 使用了unsafe 的类

public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

再点开getAndAddInt进去看Unsafe类的实现源码

里面最重要的核心方法 compareAndSwap-CAS,

public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}

getAndAddInt(Object var1, long var2, int var4)方法详解

  • var1: 是传的对象,本例是count对象,count.getAndAddInt()
  • var2: 是当前的值, 2+1=3,则var2 = 2
  • var4: 2+1=3,则var4 = 1
  • var5: 是底层当前的值,如果没有其他线程处理 – 返回 2,不加

点开compareAndSwapInt(var1, var2, var5, var5 + var4));核心

其方法的原理是:

工作内存和主内存的关系,做同步操作才一样

  • count : 工作内存

  • var5 : 底层主内存
    compareAndSwapInt(var1, var2, var5, var5: var5 + var4)
    拿当前对象里面的值和底层的值作对比,若一样,则执行加操作:var5: var5 + var4 并覆盖,
    否则一直循环,直到相同才执行操作。

    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
    compareAndSwapInt(var1, var2, var5, var5: var5 + var4)

  • var1: 是传的对象,本例是count对象,count.getAndAddInt()

  • var2: 是当前的值, 2+1=3,则var2 = 2

  • var4: 2+1=3,则var4 = 1

  • var5: 是底层当前的值,如果没有其他线程处理 – 返回 2,不加
    可以看到他是不断的死循环

AtomicLong 和 LongAdder 的比较

compareAndSet

还与一个方法,通过设置boolean,保证一个时间只有一个县城访问

AtomicReference<xxx数据类型>

AtomicReference和AtomicLong的作用相似,核心是:

AtomicXXXFieldUpdater / AtomicReferenceFieldUpdater

AtomicXXXFieldUpdater核心是:修改某一个类的某一个变量或者字段名称,
但是这个变量必须是以volatile的 ,是以非static关键字修饰的才可以(用的不是特别多)

AtomicStampReference: 解决CAS的 ABA 问题

相比其他方法,多了stamp版本,是有每个线程去负责维护的。
直接看核心代码
所以他的解决aba问题的思路是:每次变量更新的时候把变量的版本号加一,每次变量改变,版本号递增。
A:1版本
B:2版本
A:3版本

/*** Atomically sets the value of both the reference and stamp

  • to the given update values if the

  • current reference is {@code ==} to the expected reference

  • and the current stamp is equal to the expected stamp.

  • @param expectedReference the expected value of the reference

  • @param newReference the new value for the reference

  • @param expectedStamp the expected value of the stamp

  • @param newStamp the new value for the stamp

  • @return {@code true} if successful
    */

    public boolean compareAndSet(V expectedReference,
    V newReference,
    int expectedStamp,
    int newStamp) {
    Pair current = pair;
    return
    expectedReference == current.reference &&
    expectedStamp == current.stamp &&
    ((newReference == current.reference &&
    newStamp == current.stamp) ||
    casPair(current, Pair.of(newReference, newStamp)));
    }

AtomicLongArray,

相比AtomicLong,AtomicInteger额外多一个索引值去更新
相比AtomicLong,它维护一个数组,可以选择性的更改索引的某个值,

public final boolean compareAndSet(int i, long expect, long update) {
    return compareAndSetRaw(checkedByteOffset(i), expect, update);
}

AtomicBoolean

  • Atomically sets the value to the given updated value

  • if the current value {@code ==} the expected value.

  • @param expect the expected value

  • @param update the new value

  • @return {@code true} if successful. False return indicates that

  • the actual value was not equal to the expected value.

    public final boolean compareAndSet(boolean expect, boolean update) {
    int e = expect ? 1 : 0;
    int u = update ? 1 : 0;
    return unsafe.compareAndSwapInt(this, valueOffset, e, u);
    }

实现方法二 - 锁

除了atomic包, 还有sychronized 和 lock,也可以实现原子性

sychronized 依赖于JVM

sychronized 的使用

  • 修饰代码块:作用于调用这个东东的对象
  • 修饰方法: 作用于调用这个东东的对象
  • 修饰静态方法:作用于这个类的所有对象
  • 修饰类:作用于这个类的所有对象

Lock : 依赖于特殊的CPU指令,代码实现有ReentrantLock

注意

  • 如果父类有的方法有synchronized修饰的, 当一个类继承父类的时候,子类的方法里不带有synchronized这个关键字。
    因为synchronized 不属于方法声明

总结:原子性-对比

  • synchronized:不可中断锁,适合竞争不激烈,可读性好
  • Lock:可中断锁,unlock解锁,多样化同步,竞争激烈时候能维持常态
  • Atomic:竞争激烈时候能维持常态,性能比Lock还好

原创不易,一分也是爱

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_37156901/article/details/85858676