Java 深入分析

Java 深入分析 - 并发 原子量

2017-08-06  本文已影响0人  林柚柚_

Volitale

并发中,锁主要提供两种特性:互斥可见

并发情况下,一个线程先到来,给对象上锁,其他线程只能在后面排队,这种互斥带给我们的是操作的 原子性,即在并发的情况下,一个操作不会受其他线程的影响

另外,每个线程在读取变量时,自己都会缓存一份,也就是说,可能下次读取操作便不会经过内存,而是直接取缓存,当然,这在并发条件下,线程共享的变量会有问题是理所当然的,如果每个线程在访问一个变量时,每次都从内存中重新读取值,那么我们认为这个变量对于每个线程来说具有 可见性

当然,若你不需要互斥,只需要保证可见性,Java 提供了一种更轻量的语法 volitale,经过 volitale 修饰的变量,可以保证:

在执行代码时,代码顺序为 A -> B -> C,CPU 为了执行效率,可能会将其顺序打乱,当然,为什么我们在单线程条件下执行却是有序的,原因是指令重排遵循 as-if-serial 语义,即无论如何重排,都不会影响结果,就像如果 C 依赖于 AB,那么总是会在 A
B 都执行完后,才会执行 A

显而易见的是 volitale 并不支持原子性,就像 i++ 的执行过程:

volitale 有一种写法叫 读 - 写锁,它本身是矛盾的,在我们使用这种写法时,我们认为读取应该可以是不严谨的,如果超越了该模式的最基本应用,结合这两个竞争的同步机制将变得非常困难

    private volatile int value;

    public int getValue() { return value; }

    public synchronized int increment() {
        return value++;
    }

Unsafe

正如它的名字一样,Unsafe 是一个非常不安全的类

并发包下 Unsafe 提供以下功能:

我们可以认为,Jdk 并发包是在 Unsafe 下建立的,它的构造函数是被类加载器封死的,外包下可以通过反射获取,但使用 Unsafe 类,请务必对它十分清楚

Atomic 系列类

如果我们想为自己的对象原子化,AtomicReference 是一种很好的选择,AtomicReference 内部已经维护好了一个 Unsafe内存偏移地址,也开放了一些原子操作的入口

public class AtomicReference<V> implements java.io.Serializable {

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;
    private volatile V value;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicReference.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    // ...
}

如果我们想为自己的对象数组原子化,AtomicReferenceArray 是一种很好的选择

如果我们想为自己的数字变量原子化,可以选择一系列的 AtomicIntegerAtomicDouble ...

它们的实现大致相同,内部都是基于 Unsafe 构建的,我们在这里就不一一详细讨论了


  1. 无锁自旋同步 CAS 操作详解

上一篇 下一篇

猜你喜欢

热点阅读