摘要:
锁的本质是什么
CAS如何实现?如何处理 ABA 问题
互斥锁:同一时刻,只能有一个线程持有锁
原始的线程通讯 -> o.wait(); o.notify(); 在调用 o.wait(); 时,线程会进入等待队列;
CAS = compareAndSet/compareAndSwap - 自旋锁: 失败时候重试
自旋锁引发的 ABA 问题: int m = 0; // 线程①取 m 值运算,经过运算后将 m 更新为 1, 更新之前会判断 m 是否仍然为 0, 如果为0, 则将 1 更新上去; // 但是线程①并不能保证这个 0 还是曾经那个少年。因为中间有可能有其他线程将它改成了 3, 又有其他线程将 3 改回了 0。 这就是 ABA 问题; // 解决办法:加版本控制自旋锁一定比重量级锁效率高吗?
不一定,自旋锁的实现相当于 while 循环,需要消耗CPU资源。然而加重量级锁的线程在 wait,并不消耗 CPU资源。当线程较少,处理速度较快的场景,使用 CAS 效率较高。
CAS 如何保证原子性
// CAS 本身是不满足原子性的: int m = 0; public void doSth() { int current = m; // ... doSomething(); if (m == current) { m = 1; } else { delayFewTime(); doSth(); } } /* CAS的实现可以简单抽象为以上方法,关键的比较和赋值操作并不能保证原子性 有可能比较通过后赋值前,有线程更改了 m 的值 */Java 中的 java.util.concurrent.atomic 包下面的原子类,底层使用了 CAS 来保证原子性
AtomicInteger num = new AtomicInteger(0); num.incrementAndGet(); /** * Atomically updates Java variable to {@code x} if it is currently * holding {@code expected}. * * <p>This operation has memory semantics of a {@code volatile} read * and write. Corresponds to C11 atomic_compare_exchange_strong. * * @return {@code true} if successful */ @HotSpotIntrinsicCandidate public final native boolean compareAndSetInt(Object o, long offset, int expected, int x);查看HotSpot源码:jdk8u: unsafe.cpp:
cmpxchg = compare and exchange
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) UnsafeWrapper("Unsafe_CompareAndSwapInt"); oop p = JNIHandles::resolve(obj); jint* addr = (jint *) index_oop_from_field_offset_long(p, offset); return (jint)(Atomic::cmpxchg(x, addr, e)) == e; UNSAFE_ENDjdk8u: atomic_linux_x86.inline.hpp
is_MP = Multi Processor
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { int mp = os::is_MP(); __asm__ volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)" : "=a" (exchange_value) : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp) : "cc", "memory"); return exchange_value; }jdk8u: os.hpp is_MP()
static inline bool is_MP() { // During bootstrap if _processor_count is not yet initialized // we claim to be MP as that is safest. If any platform has a // stub generator that might be triggered in this phase and for // which being declared MP when in fact not, is a problem - then // the bootstrap routine for the stub generator needs to check // the processor count directly and leave the bootstrap routine // in place until called after initialization has ocurred. return (_processor_count != 1) || AssumeMP; }jdk8u: atomic_linux_x86.inline.hpp
#define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: "最终实现:
cmpxchg = cas修改变量值
lock cmpxchg 指令硬件:
lock指令在执行后面指令的时候锁定一个北桥信号
(不采用锁总线的方式)