摘要:
鎖的本質是什麼
CAS如何實現?如何處理 ABA 問題
互斥鎖:同一時刻,只能有一個線程持有鎖
原始的線程通訊 -> o.wait(); o.notify();
在調用 o.wait();
時,線程會進入等待隊列;
CAS
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_END
jdk8u: 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
指令在執行後面指令的時候鎖定一個北橋信號
(不採用鎖總線的方式)