摘要:并發(fā)同步控制遇到并發(fā)時(shí),我們避免不了要談并發(fā)控制。它會(huì)阻塞其它的線程執(zhí)行,如果當(dāng)前線程一直持有的監(jiān)控鎖,就會(huì)把其它線程一直阻塞下去。如果此時(shí)線程和線程同時(shí)進(jìn)入方法,用一段語言描述方法的執(zhí)行過程,可能是這樣子。
并發(fā)同步控制
遇到并發(fā)時(shí),我們避免不了要談并發(fā)控制。在Java語言中,我們談并發(fā)時(shí),要談到Object的監(jiān)控鎖。在MySQL的數(shù)據(jù)庫(kù)并發(fā)中,我們也要談到mysql的鎖機(jī)制。
這樣說,說到并發(fā)就避免不了鎖的概念,不管是在像Java這種語言還是MySQL這樣的數(shù)據(jù)庫(kù)產(chǎn)品,我們都是利用鎖進(jìn)行并發(fā)控制,或者說是同步控制。
控制并發(fā)的兩種方式(樂觀鎖與悲觀鎖)我們一般都可以想到或者可以理解的簡(jiǎn)單解決辦法就是,把并發(fā)轉(zhuǎn)成串行的執(zhí)行的辦法。在簡(jiǎn)單的串行情況,不存在并發(fā)的問題,那我們自然也就不存在鎖的概念了。拿Java的線程同步來說,如果有一個(gè)變量 a = 1 此時(shí)如果有兩個(gè)線程修改同執(zhí)行下面操作
a = 2; a = 0;
那么我們一般可以通過下面形式進(jìn)行解決
public final static Object writeMonitor = new Object() void setA(int a){ synchronize(writeMonitor){ this.a = a; } }
此時(shí),兩個(gè)線程只有一個(gè)線程執(zhí)行完上面步驟后,才會(huì)允許下一個(gè)線程執(zhí)行。這就是把并發(fā)轉(zhuǎn)為串行的列子。它會(huì)阻塞其它的線程執(zhí)行,如果當(dāng)前線程一直持有的writeMonitor監(jiān)控鎖,就會(huì)把其它線程一直阻塞下去。這種并發(fā)控制的鎖,我們一般稱為悲觀鎖。對(duì)應(yīng)的MySQL的Innodb引擎來說,我們利用Innodb的行鎖就是悲觀鎖的一種方式,但實(shí)際生產(chǎn)環(huán)境中,我們會(huì)很少使用它的行鎖,即很少用悲觀鎖去解決數(shù)據(jù)庫(kù)的并發(fā)問題。
在悲觀鎖的這種控制情況下,我們可以理解為:?jiǎn)栴}總是很糟糕,只能以最粗暴也最簡(jiǎn)單的解決方式,就是所有的并發(fā)都給我一個(gè)一個(gè)執(zhí)行。這種方式在某些場(chǎng)景確實(shí)很有用,比如redis的并發(fā)控制就是這么實(shí)現(xiàn)。
相對(duì)于悲觀鎖這種方式,還會(huì)有另外一種解決并發(fā)的辦法。還以Java語言中的一些設(shè)計(jì)來談。在Java的并發(fā)工具包JUC下有個(gè)atomic包,比如AtomicIntegr,我們知道這些封裝好的類都是線程安全的工具類,可以直接在多線程環(huán)境下使用,說下getAndSet方法
public final int getAndSet(int newValue){ for( ;; ){ int current = get(); if(compareAndSet(current,newValue)){ return current; } } }
在這個(gè)方法中,我們并沒有看到synchronize關(guān)鍵字,代碼也很簡(jiǎn)潔。關(guān)鍵的地方在與compareAndSet(current,newValue)這個(gè)方法的設(shè)計(jì)。在Java中它是一個(gè)本地方法,所以我們看不到它的具體實(shí)現(xiàn)??梢匀oogle下查看具體的設(shè)計(jì),這里我說下我的認(rèn)識(shí)。如果有個(gè)變量 a = 1 此時(shí)有兩個(gè)線程同時(shí)執(zhí)行下面操作:
a = 1; a = 2;
為了下面可以方便簡(jiǎn)單的描述問題,我們認(rèn)為對(duì)變量的更改是原子性的,即不談Java的內(nèi)存模型問題或忽視線程可見問題。如果此時(shí)線程A和線程B同時(shí)進(jìn)入getAndSet方法,用一段語言描述compareAndSet方法的執(zhí)行過程,可能是這樣子。
A線程讀取到current = 0; B線程讀取到current = 0; A執(zhí)行set方法,先去把自己current值和內(nèi)存中現(xiàn)有的值(我們把該值成為memory)比較,發(fā)現(xiàn) current = memory = 0, A線程更改 a 成功,此時(shí)memory = 1 這時(shí)B線程執(zhí)行set方法,則會(huì)有這樣的 current != memory ,B線程更新失敗。 于是B線程重新進(jìn)行,此時(shí)獲取current = 1,在執(zhí)行set方法,current = memory = 1 ,OK B線程也執(zhí)行成功。 最終 memory = 2;
這種并發(fā)控制和上述悲觀鎖的并發(fā)控制方式,主要區(qū)別就是,沒有阻塞。它不會(huì)阻塞其它并發(fā)的操作行為,而是讓他們嘗試更新。這種嘗試的去更新的控制形式,我們叫它樂觀鎖。樂觀的說法就是體現(xiàn)在不會(huì)阻塞其它并發(fā)者。這種樂觀鎖在實(shí)際電商業(yè)務(wù)中則很常見,比如更新庫(kù)存,比如hibernate的樂觀鎖實(shí)現(xiàn)。
數(shù)據(jù)庫(kù)的悲觀鎖和樂觀鎖并發(fā)控制在數(shù)據(jù)庫(kù)中,以mysql的Innodb引擎為例,下面語句就是悲觀鎖的使用方式
start transaction; select * from message where id= 1 for update; update set … where id = 1; commit;
上述for update會(huì)鎖住id =1的這行數(shù)據(jù),它會(huì)阻塞其它連接查詢改行數(shù)據(jù)。在實(shí)際生成環(huán)境中卻很少使用。以電影院的售票系統(tǒng)來講,在用戶并發(fā)購(gòu)買座位時(shí),肯定會(huì)存在并發(fā)購(gòu)買的問題。這時(shí)一般我們都會(huì)通過增加一個(gè)version字段來解決問題
start transaction; // ticket 代表電影票下的座位分布信息,status = 1 代表座位已經(jīng)被預(yù)定。 update ticket set `status` = 1 ,version = 1 where id =1 and version =0; commit;
這時(shí)version字段就相當(dāng)于并發(fā)訪問下的版本控制如果有人預(yù)定version的字段就變?yōu)?,如果發(fā)現(xiàn)設(shè)置status字段為1時(shí)version不是1就更新失敗,這就是通過樂觀鎖的方式進(jìn)行并發(fā)控制的一種方式。上述語句其實(shí)也可以不增加version字段,這里主要方便敘述問題,直接寫成下面這樣也OK
start transaction; update ticket set `status` = 1 where status = 0; commit;
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/65861.html
摘要:比如主協(xié)程啟動(dòng)個(gè)子協(xié)程,主協(xié)程等待所有子協(xié)程退出后再繼續(xù)后續(xù)流程,這種場(chǎng)景下也可輕易實(shí)現(xiàn)。這個(gè)例子中,父協(xié)程僅僅是等待子協(xié)程結(jié)束,其實(shí)父協(xié)程也可以向管道中寫入數(shù)據(jù)通知子協(xié)程結(jié)束,這時(shí)子協(xié)程需要定期地探測(cè)管道中是否有消息出現(xiàn)。一.設(shè)計(jì)原理Go 語言中最常見的、也是經(jīng)常被人提及的設(shè)計(jì)模式就是:不要通過共享內(nèi)存來通信,我們應(yīng)該使用通信來共享內(nèi)存通過共享內(nèi)存來通信是直接讀取內(nèi)存的數(shù)據(jù),而通過通信來共...
摘要:故事開始了,小程序圖片合成真機(jī)測(cè)試時(shí),會(huì)報(bào)錯(cuò)。所以只能將異步并發(fā)改為同步阻塞式渲染。 showImg(https://segmentfault.com/img/remote/1460000013228074); 故事開始了,小程序canvas圖片合成 真機(jī)測(cè)試時(shí),會(huì)報(bào)錯(cuò):getImageInfo failed 。也就是說,我這邊異步請(qǐng)求50張圖片,每張圖片都是通過getImageInf...
摘要:而是在調(diào)用發(fā)出后,被調(diào)用者通過狀態(tài)通知來通知調(diào)用者,或通過回調(diào)函數(shù)處理這個(gè)調(diào)用。請(qǐng)求程序發(fā)出請(qǐng)求,從服務(wù)器端獲取數(shù)據(jù),并設(shè)置了回調(diào)函數(shù)。然后,瀏覽器會(huì)設(shè)置偵聽來自網(wǎng)絡(luò)的響應(yīng),拿到數(shù)據(jù)后,將該回調(diào)函數(shù)插入到事件循環(huán)。 并發(fā)與并行 并發(fā)是指兩個(gè)或多個(gè)事件鏈隨時(shí)間發(fā)展交替執(zhí)行,以至于從更高的層次來看,就像是同時(shí)運(yùn)行(但在任意時(shí)刻只處理一個(gè)事件) 并發(fā)的關(guān)鍵是你有處理多個(gè)任務(wù)的能力,不一定同...
摘要:在接下來的分鐘,你將會(huì)學(xué)會(huì)如何通過同步關(guān)鍵字,鎖和信號(hào)量來同步訪問共享可變變量。所以在使用樂觀鎖時(shí),你需要每次在訪問任何共享可變變量之后都要檢查鎖,來確保讀鎖仍然有效。 原文:Java 8 Concurrency Tutorial: Synchronization and Locks譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 歡迎閱讀我的Java8并發(fā)教程的第二部分。這份指南將...
閱讀 3937·2021-11-24 09:39
閱讀 3842·2021-11-22 12:07
閱讀 1181·2021-11-04 16:10
閱讀 922·2021-09-07 09:59
閱讀 1966·2019-08-30 15:55
閱讀 1008·2019-08-30 15:54
閱讀 794·2019-08-29 14:06
閱讀 2541·2019-08-27 10:54