Java鎖初步

摘要:

鎖的本質是什麼

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指令在執行後面指令的時候鎖定一個北橋信號

(不採用鎖總線的方式)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章