JAVA ReentrantLock可見性?
正如官網上所說的ReentrantLock與sync的語義是一致的,也就是說ReentrantLock也具有一致性、可見性。通過源碼可知,在最極端的情況下ReentrantLock的lock、unlock只會對volatile 修飾的 state 變數操作,並不會涉及到其他太多的native代碼。
我想問的是:ReentrantLock 是如何保證臨界區代碼的可見性的?static int x = 0;
ReentrantLock _LOCK = new ReentrantLock();public static void inc(){_LOCK.lock();
x++; _LOCK.unlock();}這個靜態變數x是如何對其他線程可見的?
賜教。
volatile修飾的state保證了lock和unlock的Happens-Before的偏序關係,根據volatile規則:volatile變數的寫入必須在讀取之前執行,保證了可重入鎖ReentrantLock的unlock方法必須在lock方法返回前執行。
關於可見性,參照lock和unlock的源碼,可以追溯到與之相對應的Parker::park和Parker::unpark函數的實現:(貼出HotSpotVM bsd平台的實現版本)
void Parker::park(bool isAbsolute, jlong time) {
// Ideally we"d do something useful while spinning, such
// as calling unpackTime().
// Optional fast-path check:
// Return immediately if a permit is available.
// We depend on Atomic::xchg() having full barrier semantics
// since we are doing a lock-free update to _counter.
if (Atomic::xchg(0, _counter) &> 0) return;
Thread* thread = Thread::current();
assert(thread-&>is_Java_thread(), "Must be JavaThread");
JavaThread *jt = (JavaThread *)thread;
// Optional optimization -- avoid state transitions if there"s an interrupt pending.
// Check interrupt before trying to wait
if (Thread::is_interrupted(thread, false)) {
return;
}
// Next, demultiplex/decode time arguments
struct timespec absTime;
if (time &< 0 || (isAbsolute time == 0) ) { // don"t wait at all
return;
}
if (time &> 0) {
unpackTime(absTime, isAbsolute, time);
}
// Enter safepoint region
// Beware of deadlocks such as 6317397.
// The per-thread Parker:: mutex is a classic leaf-lock.
// In particular a thread must never block on the Threads_lock while
// holding the Parker:: mutex. If safepoints are pending both the
// the ThreadBlockInVM() CTOR and DTOR may grab Threads_lock.
ThreadBlockInVM tbivm(jt);
// Don"t wait if cannot get lock since interference arises from
// unblocking. Also. check interrupt before trying wait
if (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) != 0) {
return;
}
int status ;
if (_counter &> 0) { // no wait needed
_counter = 0;
status = pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant") ;
// Paranoia to ensure our locked and lock-free paths interact
// correctly with each other and Java-level accesses.
OrderAccess::fence();
return;
}
#ifdef ASSERT
// Don"t catch signals while blocked; let the running threads have the signals.
// (This allows a debugger to break into the running thread.)
sigset_t oldsigs;
sigset_t* allowdebug_blocked = os::Bsd::allowdebug_blocked_signals();
pthread_sigmask(SIG_BLOCK, allowdebug_blocked, oldsigs);
#endif
OSThreadWaitState osts(thread-&>osthread(), false /* not Object.wait() */);
jt-&>set_suspend_equivalent();
// cleared by handle_special_suspend_equivalent_condition() or java_suspend_self()
if (time == 0) {
status = pthread_cond_wait (_cond, _mutex) ;
} else {
status = os::Bsd::safe_cond_timedwait (_cond, _mutex, absTime) ;
if (status != 0 WorkAroundNPTLTimedWaitHang) {
pthread_cond_destroy (_cond) ;
pthread_cond_init (_cond, NULL);
}
}
assert_status(status == 0 || status == EINTR ||
status == ETIMEDOUT,
status, "cond_timedwait");
#ifdef ASSERT
pthread_sigmask(SIG_SETMASK, oldsigs, NULL);
#endif
_counter = 0 ;
status = pthread_mutex_unlock(_mutex) ;
assert_status(status == 0, status, "invariant") ;
// Paranoia to ensure our locked and lock-free paths interact
// correctly with each other and Java-level accesses.
OrderAccess::fence();
// If externally suspended while waiting, re-suspend
if (jt-&>handle_special_suspend_equivalent_condition()) {
jt-&>java_suspend_self();
}
}
void Parker::unpark() {
int s, status ;
status = pthread_mutex_lock(_mutex);
assert (status == 0, "invariant") ;
s = _counter;
_counter = 1;
if (s &< 1) {
if (WorkAroundNPTLTimedWaitHang) {
status = pthread_cond_signal (_cond) ;
assert (status == 0, "invariant") ;
status = pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant") ;
} else {
status = pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant") ;
status = pthread_cond_signal (_cond) ;
assert (status == 0, "invariant") ;
}
} else {
pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant") ;
}
可以看到unpark函數調用後,通過pthread_cond_signal喚醒被阻塞線程,被阻塞線程獲得_mutex鎖後執行pthread_cond_wait後的代碼,在pthread_mutex_unlock之後,顯示地調用了OrderAccess::fence()這個函數;這個函數是HotSpot VM對JMM的內存屏障一個具體的實現函數; 我們來看看它的實現,在此還是貼出bsd x86的實現版本:
inline void OrderAccess::fence() {
if (os::is_MP()) {
// always use locked addl since mfence is sometimes expensive
#ifdef AMD64
__asm__ volatile ("lock; addl $0,0(%%rsp)" : : : "cc", "memory");
#else
__asm__ volatile ("lock; addl $0,0(%%esp)" : : : "cc", "memory");
#endif
}
}
這裡針對多處理器(MP)平台,採用內嵌彙編的實現,使用volatile阻止編譯器優化,最後使用lock; addl $0,0(%%rsp)的方式代替mfence指令實現內存屏障的功能,原因注釋寫得很清楚,mfence指令有時過於昂貴。
HotSpot VM 對於volatile的field進行put_field和get_field時,也會顯示地調用OrderAccess的MOAM的函數實現,例如:put_field指令,就調用了OrderAccess::storeload()。
————————————————————
在此給出自己的一知半解,歡迎大家繼續深挖!
並發編程網 - ifeve.com
可見性不是指對一個變數修改後,其它線程馬上能看到這個值,而是一個相對的概念。
舉個例子,假定兩個變數A,B,初始值都是0;
線程1:
A = 1;(1)
B = 1;(2)
線程2:
if (B == 1) (3)
A = ? (4)
此處A的值可能是1,也可能是0,由於CPU的亂序執行(不同的CPU架構有不同的亂序模型)。
如果將B定義為volatile呢?此處A的值只能是1。因為JMM的volatile語義限制了CPU的亂序行為,volatile寫限制之前的代碼跑到volatile寫之前執行,volatile讀限制之後的代碼在volatile讀之前執行。這裡語句3確保發生在語句2之後,推斷出語句4發生在語句1之後,也就是happens-before關係。lock也有相同的語義,語句2相當於unlock,語句3相當於lock。
所以JMM要求我們對存在數據競爭的變數訪問用同步元語(volatile,lock等)進行同步,同步限制了亂序執行,也就是確保了可見性。
可以看下這篇文章《Java鎖是如何保證數據可見性的》:並發編程網 - ifeve.com
樓主問的是可見性,樓上們別瞎答,簡述吧,寫太多樓主該暈了
用volatile的happenbefore性質百度可查
lock和unlock方法中有對volatile變數的讀取和寫入,所以你的x就被happenbefore包圍了,也就能被其他縣城讀到推薦閱讀:
※為什麼有人想要成為程序員?
※金融女,怎樣能認識到聰明有趣的程序員?
※寫Python的時候總是用別人的庫,這樣對提高代碼能力有幫助嗎?
※碼農生活規律嗎?
※為什麼公開的一些代碼的注釋真的好少,沒有注釋我們這些新手好難玩?
TAG:程序員 | Java | Java虛擬機JVM | 編譯原理 |