Java 并发第二篇

70 阅读3分钟

CAS

CAS Compare And Swap 比较并交换

是现代 CPU 广泛支持的一种对内存中共享数据进行操作的一种特殊指令。

CAS的作用:可以将比较和交换转换成原子操作,这个原子操作直接有 CPU 保证

可以保证共享变量赋值时的原子操作, CAS 操作依赖三个值,

  • 内存中的值 V

  • 旧的预估值 X

  • 要修改的新值 B

  • 如果旧的预估值 X 等于内存中的值 V ,就将新的值 B 保存到内存中。

CAS 原理

Unsafe 类介绍


class AtomicInteger extends Number implements java.io.Serializable {

    private static final long valueOffset
    private static final Unsafe Unsafe = Unsafe。getUnsafe();
    
    private volatile int value;

}

image.png

乐观锁: CAS 机制

悲观锁: synchornized

JDK 6 synchronized 优化

synchronized 锁升级

JDK 1.6 改进优化,包括偏向锁、轻量级锁和适应性自旋、锁消除、锁粗化等,

这些技术都是为了在线程之间更高效地共享数据、以及解决竞争问题,从而提高程序的执行效率。

image.png

无锁 -> 偏向锁 -> 轻量级锁和适应性自旋 -> 重量级锁

Java 对象的布局

学习 Java 对象的布局,在JVM 中, 对象在内存中的布局分三块区域:

  • 对象头
  • 实例数据
  • 对齐数据。

image.png

markOop_mark mark word

image.png

klass pointer

对象头 = mark word + 类型指针(未开启指针压缩的情况下)

在32位系统中 Mark Word = 4 bytes ,类型指针 = 4bytes, 对象头 = 8 bytes

在64位系统中, Mark Word = 8 bytes , 类型指针 = 8 bytes, 对象头 = 16 bytes

偏向锁

UseBiasedLocking = false 参数关闭偏向锁

大多数情况下,锁不仅不存在多线程竞争,而且总是有同一个线程多次获得, 为了让线程获得锁的代价减小, 偏向锁关键在偏字

偏向锁的原理

当线程第一次访问同步代码快并获取锁时, 偏向锁的处理流程如下:

1、检测 Mark Word 是否为可偏向状态, 是否为偏向锁1 , 锁标识位为01

2、若为可偏向状态,则测试线程 ID 是否为当前线程 ID ,如果是,执行同步代码快,否则执行步骤3

3、如果测试线程 ID 不为当前线程 ID, 则通过 CAS 操作将 Mark Word 的线程 ID 替换为当前线程 ID ,执行同步代码快;

偏向锁在一个线程下执行效率高;

偏向锁的撤销

1、 偏向锁的撤销动作必须等待全局安全点

2、 暂停拥有偏向的线程,判断锁对象是否处在锁定状态

3、 撤销偏向锁,恢复到无锁(标志位01)或轻量级锁(标志位00)的状态

轻量级锁

目的:在多线程交替执行同步代码快的情况下,尽量避免重量级锁引起的性能消耗,但是如果多个线程在同一个时刻进入临界区,会导致轻量级锁膨胀升级重量级锁,所以在轻量级锁的出现并非代替重量级锁

原理:未写完

总结

image.png

synchronized 优化方法

尽量使用同步代码快

减少 synchronized 的范围 减少锁的竞争

Looper.getMainLooper(); 通过代码块 而不是修饰方法


/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. See also: {@link #prepare()}
 *
 * @deprecated The main looper for your application is created by the Android environment,
 *   so you should never need to call this function yourself.
 */
@Deprecated
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

/**
 * Returns the application's main looper, which lives in the main thread of the application.
 */
public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}

减少 synchronized 的粒度

尽量使用对象而不要使用 class 类型


synchronized (lock) {
  queue.remove(priority);
  highestPriority = queue.isEmpty() ? Integer.MIN_VALUE : Util.castNonNull(queue.peek());
  lock.notifyAll();
}

ConcurrentHashMap 读写分离

HashTable :锁定整个哈希表,一个操作正在进行时, 其他操作也同时锁定,效率低下

image.png

ConcurrentHashMap :局部锁定,只锁定桶,对当前元素锁定,其他元素不锁定

image.png

image.png

总结: 读写分离

读取时不加锁,写入和删除时加锁: 同样的集合例子有:

ConcurrentHashMap , CopyOnWriteArrayList 和 CopyOnWriteSet