摘要:
锁的本质是什么
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
指令在执行后面指令的时候锁定一个北桥信号
(不采用锁总线的方式)