Java實(shí)現(xiàn)多線程數(shù)據(jù)同步的幾種方法

1. 應(yīng)用背景

程序在設(shè)計(jì)當(dāng)中如果采取多線程操作的時(shí)候景殷,如果操作的對(duì)象是一個(gè)的話,由于多個(gè)線程共享同一塊內(nèi)存空間充石,因此經(jīng)常會(huì)遇到數(shù)據(jù)安全訪問的問題贱纠,下面看一個(gè)經(jīng)典的問題峻厚,銀行取錢的問題:

1)、你有一張銀行卡谆焊,里面有5000塊錢惠桃,然后你到取款機(jī)取款,取出3000辖试,當(dāng)正在取的時(shí)候辜王,取款機(jī)已經(jīng)查詢到你有5000塊錢,然后正準(zhǔn)備減去300塊錢的時(shí)候

2)罐孝、你的老婆拿著那張銀行卡對(duì)應(yīng)的存折到銀行取錢呐馆,也要取3000.然后銀行的系統(tǒng)查詢,存折賬戶里還有6000(因?yàn)樯厦驽X還沒扣)莲兢,所以它也準(zhǔn)備減去3000汹来,

3)续膳、你的卡里面減去3000,5000-3000=2000收班,并且你老婆的存折也是5000-3000=2000坟岔。

4)、結(jié)果摔桦,你們一共取了6000社付,但是卡里還剩下2000。

不難發(fā)現(xiàn)邻耕,當(dāng)多個(gè)線程訪問同一數(shù)據(jù)并操作的時(shí)候非常容易出現(xiàn)類似的問題瘦穆,。為了避免這樣的事情發(fā)生赊豌,我們要保證線程同步互斥,所謂同步互斥就是:并發(fā)執(zhí)行的多個(gè)線程在某一時(shí)間內(nèi)只允許一個(gè)線程在執(zhí)行以訪問共享數(shù)據(jù)绵咱。

2.同步互斥鎖

同步鎖原理:Java會(huì)為每個(gè)對(duì)象內(nèi)置同步鎖碘饼,通過使用synchronized來獲取一個(gè)對(duì)象的同步鎖,synchronized的使用方式悲伶,是在一段代碼塊中艾恼,加上synchronized(object){ ... }

當(dāng)線程首次執(zhí)行到synchronized語句塊時(shí)候會(huì)獲得對(duì)象的同步鎖(鎖最開始屬于對(duì)象,后被線程持有)麸锉,在當(dāng)前線程不釋放同步鎖時(shí)候钠绍,其他線程獲取該對(duì)象同步鎖的行為是被阻塞的,直到該鎖被釋放花沉。以下幾種情況下柳爽,線程才會(huì)釋放掉對(duì)象的同步鎖

1.線程執(zhí)行完synchronized修飾的語句塊。

2.線程主動(dòng)執(zhí)行wait()來釋放同步鎖碱屁。

同步鎖雖然可以解決多并發(fā)引起的數(shù)據(jù)安全問題磷脯,但是會(huì)在一定程度上影響程序運(yùn)行的效率娩脾,也會(huì)引起死鎖問題赵誓。因此慎重使用俩功。下面是別人描述的死鎖,做引用碰声。

死鎖:多個(gè)線程同時(shí)被阻塞,它們中的一個(gè)或者全部都在等待某個(gè)資源被釋放奥邮。由于線程被無限期地阻塞罗珍,因此程序不能正常運(yùn)行。簡(jiǎn)單的說就是:線程死鎖時(shí)覆旱,第一個(gè)線程等待第二個(gè)線程釋放資源,而同時(shí)第二個(gè)線程又在等待第一個(gè)線程釋放資源扣唱。這里舉一個(gè)通俗的例子:如在人行道上兩個(gè)人迎面相遇,為了給對(duì)方讓道团南,兩人同時(shí)向一側(cè)邁出一步噪沙,雙方無法通過,又同時(shí)向另一側(cè)邁出一步吐根,這樣還是無法通過正歼。假設(shè)這種情況一直持續(xù)下去,這樣就會(huì)發(fā)生死鎖現(xiàn)象拷橘。

導(dǎo)致死鎖的根源在于不適當(dāng)?shù)剡\(yùn)用“synchronized”關(guān)鍵詞來管理線程對(duì)特定對(duì)象的訪問局义。“synchronized”關(guān)鍵詞的作用是冗疮,確保在某個(gè)時(shí)刻只有一個(gè)線程被允許執(zhí)行特定的代碼塊萄唇,因此,被允許執(zhí)行的線程首先必須擁有對(duì)變量或?qū)ο蟮呐潘栽L問權(quán)术幔。當(dāng)線程訪問對(duì)象時(shí)另萤,線程會(huì)給對(duì)象加鎖,而這個(gè)鎖導(dǎo)致其它也想訪問同一對(duì)象的線程被阻塞诅挑,直至第一個(gè)線程釋放它加在對(duì)象上的鎖四敞。

一個(gè)死鎖的造成很簡(jiǎn)單,比如有兩個(gè)對(duì)象A 和 B 拔妥。第一個(gè)線程鎖住了A目养,然后休眠1秒,輪到第二個(gè)線程執(zhí)行毒嫡,第二個(gè)線程鎖住了B癌蚁,然后也休眠1秒,然后有輪到第一個(gè)線程執(zhí)行兜畸。第一個(gè)線程又企圖鎖住B努释,可是B已經(jīng)被第二個(gè)線程鎖定了,所以第一個(gè)線程進(jìn)入阻塞狀態(tài)咬摇,又切換到第二個(gè)線程執(zhí)行伐蒂。第二個(gè)線程又企圖鎖住A,可是A已經(jīng)被第一個(gè)線程鎖定了肛鹏,所以第二個(gè)線程也進(jìn)入阻塞狀態(tài)逸邦。就這樣恩沛,死鎖造成了。

3.顯式鎖

為了符合Java面向?qū)ο蟮脑O(shè)計(jì)原則缕减,在JDK1.5中雷客,引入了顯示鎖的概念。

程序設(shè)計(jì)過程中如果提前發(fā)現(xiàn)可能會(huì)引起多線程數(shù)據(jù)訪問的安全問題桥狡,可以通過Lock lock =newReentrantLock();來獲取一個(gè)鎖搅裙,并將該鎖作為運(yùn)行參數(shù)傳入其中,在關(guān)鍵數(shù)據(jù)塊之前使用lock.lock();來控制對(duì)競(jìng)爭(zhēng)資源并發(fā)訪問的控制裹芝,顯式鎖的優(yōu)點(diǎn)是可以知道持有鎖的對(duì)象部逮,比同步鎖也清晰好多。當(dāng)然使用完之后也要主動(dòng)釋放(lock.unlock())嫂易。

4.讀寫鎖

讀寫鎖是在顯示鎖的基礎(chǔ)上對(duì)讀寫進(jìn)行分離的一種鎖兄朋,可以認(rèn)為是為了提高并發(fā)效率的一種優(yōu)化。使用方法類比顯式鎖

初始化:ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

四種操作: rwl.readLock().lock(); ? rwl.readLock().unlock()

? ? ? ? ? ? ? ? ? ? ?rwl.writeLock().lock(); ?rwl.writeLock().unlock()

關(guān)于讀寫鎖之間的互斥:

1怜械,讀鎖是排寫鎖操作的,讀鎖不排讀鎖操作,多個(gè)讀鎖可以并發(fā)不阻塞享完。即在讀鎖獲取后和讀鎖釋放之前,寫鎖并不能被任何線程獲得彼绷,多個(gè)讀鎖同時(shí)作用期間茴迁,試圖獲取寫鎖的線程都處于等待狀態(tài)堕义,當(dāng)最后一個(gè)讀鎖釋放后,試圖獲取寫鎖的線程才有機(jī)會(huì)獲取寫鎖。

2洒擦,寫鎖是排寫鎖怕膛、排讀鎖操作的褐捻。當(dāng)一個(gè)線程獲取到寫鎖之后椅邓,其他試圖獲取寫鎖和試圖獲取讀鎖的線程都處于等待狀態(tài)景馁,直到寫鎖被釋放裁僧。

3慕购,在寫鎖狀態(tài)中,可以獲取讀鎖 即線程持有寫鎖的狀態(tài)下是可以繼續(xù)申請(qǐng)讀鎖的获洲。即一線程同時(shí)持有讀鎖和寫鎖

4贡珊,讀鎖是不能夠獲得寫鎖的涉馁,如果要加寫鎖,本線程必須釋放所持有的讀鎖寒随。

5.volatile

用volatile修飾的變量妻往,線程在每次使用變量的時(shí)候试和,都會(huì)讀取變量修改后的最的值。volatile很容易被誤用好渠,用來進(jìn)行原子性操作晦墙,可以看作是一種輕量級(jí)的synchronized肴茄,但是是盡量保證每次讀取的是最新的寡痰,并不絕對(duì)保證棋凳。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末剩岳,一起剝皮案震驚了整個(gè)濱河市拍棕,隨后出現(xiàn)的幾起案子勺良,更是在濱河造成了極大的恐慌,老刑警劉巖尚困,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谬泌,死亡現(xiàn)場(chǎng)離奇詭異逻谦,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)邦马,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嘱腥,“玉大人齿兔,你說我怎么就攤上這事》治” “怎么了医寿?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵靖秩,是天一觀的道長(zhǎng)竖瘾。 經(jīng)常有香客問我捕传,道長(zhǎng)扩劝,這世上最難降的妖魔是什么棒呛? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮催什,結(jié)果婚禮上蒲凶,老公的妹妹穿的比我還像新娘拆内。我一直安慰自己,他們只是感情好灵巧,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布刻肄。 她就那樣靜靜地躺著敏弃,像睡著了一般噪馏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瓶颠,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天粹淋,我揣著相機(jī)與錄音,去河邊找鬼欢搜。 笑死谴轮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的疮装。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼翩隧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起专缠,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤涝婉,失蹤者是張志新(化名)和其女友劉穎墩弯,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體渔工,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年脓魏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了通惫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片履腋。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡遵湖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谋国,到底是詐尸還是另有隱情芦瘾,我是刑警寧澤集畅,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布挺智,位于F島的核電站,受9級(jí)特大地震影響二鳄,放射性物質(zhì)發(fā)生泄漏沐扳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望祈餐。 院中可真熱鬧,春花似錦哄陶、人聲如沸帆阳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜒谤。三九已至,卻和暖如春至扰,著一層夾襖步出監(jiān)牢的瞬間鳍徽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工敢课, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留阶祭,地道東北人绷杜。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像濒募,于是被迫代替她去往敵國(guó)和親鞭盟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容