从分布式锁角度理解Java的synchronized关键字
分布式鎖
分布式鎖就以zookeeper為例,zookeeper是一個分布式系統(tǒng)的協(xié)調(diào)器,我們將其理解為一個文件系統(tǒng),可以在zookeeper服務(wù)器中創(chuàng)建或刪除文件夾或文件.設(shè)D為一個數(shù)據(jù)系統(tǒng),不具備事務(wù)能力,在并發(fā)狀態(tài)下可能出現(xiàn)對單個數(shù)據(jù)同時讀寫.客戶端A,B是數(shù)據(jù)系統(tǒng)D提供的客戶端,能夠?qū)ζ渥x寫.
幾個關(guān)鍵角色已經(jīng)登場,D是一個不提供事務(wù)行為的數(shù)據(jù)系統(tǒng),其存放的數(shù)據(jù)可被讀寫,在單客戶端條件下可以保證數(shù)據(jù)的可靠,但是在兩個客戶端可能并發(fā)請求時就變得不可靠,A寫的數(shù)據(jù)可能被B覆蓋,B讀的數(shù)據(jù)可能是A沒有寫完的數(shù)據(jù).在不修改D源碼為其提供原子性操作的前提下,我們可以考慮分布式鎖.
客戶端的原始API
void write(){// 寫數(shù)據(jù) } void read(){// 讀數(shù)據(jù) }如何做呢,核心就是以zookeeper為媒介.
修改客戶端API,如果讀或者寫,我們首先要在zookeeper上創(chuàng)建一個文件夾,如果創(chuàng)建成功,我們就執(zhí)行讀寫操作,如果不成功(代表已經(jīng)有人創(chuàng)建了)我們就一直嘗試創(chuàng)建,直到創(chuàng)建成功.當(dāng)創(chuàng)建成功時,對D系統(tǒng)的數(shù)據(jù)進(jìn)行讀寫操作,完成后刪除zookeeper上創(chuàng)建的文件夾并退出讀寫方法.
void write(){while(在zookeeper上創(chuàng)建文件夾) {寫操作;刪除zookeeper的剛創(chuàng)建的文件夾;return;} }這樣就完成了一個分布式并發(fā)行為的同步.假設(shè)A已經(jīng)創(chuàng)建了文件夾,B就沒辦法進(jìn)行后續(xù)操作,會一直嘗試創(chuàng)建文件夾,A執(zhí)行完操作之后刪除文件夾,B才有機(jī)會進(jìn)行在D系統(tǒng)上的操作.這個例子中產(chǎn)生了一個臨界資源:D系統(tǒng)的數(shù)據(jù),和一個競態(tài)條件:A和B并發(fā)讀寫.(實(shí)際上zookeeper中是可以為監(jiān)聽者發(fā)送文件情況的,比如我創(chuàng)建文件夾沒有成功,可以監(jiān)聽該文件夾,當(dāng)文件夾變化時會通知你,這時候你就可以再嘗試創(chuàng)建文件夾了(很像wait和notify),但是為了不把重心放在zookeeper上就沒有改成監(jiān)聽模式)
Java中Synchronized關(guān)鍵字
那我們再回到Java中的synchronized關(guān)鍵字上來,如果你還沒理解上面的行為和synchronized的關(guān)系你可以繼續(xù)往后看.
synchronized究竟鎖住的是什么呢,鎖住的是一塊內(nèi)存,我們在內(nèi)存中的某個位置設(shè)置一個值為0,當(dāng)該值為0時,一個線程為了修改臨界資源將其設(shè)置為1,然后讀寫操作,讀寫結(jié)束將其設(shè)置為0,其他線程可以再次將其改為1,進(jìn)行讀寫,結(jié)束后再將其置為0......這塊內(nèi)存存儲在每個對象的對象頭中,我們稱其為鎖標(biāo)志位(實(shí)際上所標(biāo)志位不是簡單的0和1,鎖標(biāo)志位分為四種狀態(tài):無鎖,偏向鎖,輕量級鎖,和重量級鎖,并發(fā)中還涉及鎖升級等,但本文不做細(xì)究,有興趣的讀者可以查閱相關(guān)資料).
當(dāng)進(jìn)入synchronized代碼塊或者方法的時候,你會像上面說的分布式鎖那樣"創(chuàng)建一個文件夾",其他線程無法"創(chuàng)建文件夾"就會一直去嘗試,當(dāng)代碼塊或方法結(jié)束的時候會"刪除文件夾",其他線程可以去搶奪創(chuàng)建文件夾的機(jī)會.synchronized可以看做一道門,鎖住的對象可以看做是門票.千萬不要認(rèn)為synchronized鎖住的是臨界資源,synchronized是以某個對象的鎖標(biāo)志作為入場券去約束競態(tài)線程之間行為:我只有一張門票,我只給一個人.
看一下synchronized的用法,多個線程之間的競態(tài)行為一定要是用相同的門票去約束.
// 這鎖住的是 class Foo{// 這鎖住的是類對象static synchronized void bar();// 這鎖住的是this,即實(shí)例對象synchronized void bar();// 等價于static synchronizedvoid bar(){synchronized(Foo.class){};}// 等價于synchronized實(shí)例方法void bar(){synchronized(this){}} }對于靜態(tài)方法需要注意一點(diǎn)(假如你想觀察偏向鎖的變化),實(shí)例方法可以直接觀察實(shí)例對象的對象頭上的鎖標(biāo)志位,但是靜態(tài)方法需要觀察Foo.class對象的鎖標(biāo)志位,如果觀察實(shí)例對象的鎖標(biāo)志位會竹籃打水喲.
轉(zhuǎn)載于:https://www.cnblogs.com/krcys/p/9379836.html
總結(jié)
以上是生活随笔為你收集整理的从分布式锁角度理解Java的synchronized关键字的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PotPlayer安装与配置
- 下一篇: 【目录】《剑指Offer》Java实现