日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

JAVA如何解決并發(fā)問題

瀏覽:198日期:2022-08-30 18:05:45
并發(fā)問題的根源在哪

首先,我們要知道并發(fā)要解決的是什么問題?并發(fā)要解決的是單進(jìn)程情況下硬件資源無法充分利用的問題。而造成這一問題的主要原因是CPU-內(nèi)存-磁盤三者之間速度差異實(shí)在太大。如果將CPU的速度比作火箭的速度,那么內(nèi)存的速度就像火車,而最慘的磁盤,基本上就相當(dāng)于人雙腿走路。

這樣造成的一個(gè)問題,就是CPU快速執(zhí)行完它的任務(wù)的時(shí)候,很長(zhǎng)時(shí)間都會(huì)在等待磁盤或是內(nèi)存的讀寫。

計(jì)算機(jī)的發(fā)展有一部分就是如何重復(fù)利用資源,解決硬件資源之間效率的不平衡,而后就有了多進(jìn)程,多線程的發(fā)展。并且演化出了各種為多進(jìn)程(線程)服務(wù)的東西:

CPU增加緩存機(jī)制,平衡與內(nèi)存的速度差異 增加了多個(gè)概念,CPU時(shí)間片,程序計(jì)數(shù)器,線程切換等,用以更好得服務(wù)并發(fā)場(chǎng)景 編譯器的指令優(yōu)化,希望在內(nèi)部充分利用硬件資源

但是這樣一來,也會(huì)帶來新的并發(fā)問題,歸結(jié)起來主要有三個(gè)。

由于緩存導(dǎo)致的可見性問題 線程切換帶來的原子性問題 編譯器優(yōu)化帶來的有序性問題

我們分別介紹這幾個(gè):

緩存導(dǎo)致的可見性

CPU為了平衡與內(nèi)存之間的性能差異,引入了CPU緩存,這樣CPU執(zhí)行指令修改數(shù)據(jù)的時(shí)候就可以批量直接讀寫CPU緩存的內(nèi)存,一個(gè)階段后再將數(shù)據(jù)寫回到內(nèi)存。

但由于現(xiàn)在多核CPU技術(shù)的發(fā)展,各個(gè)線程可能運(yùn)行在不同CPU核上面,每個(gè)CPU核各有各自的CPU緩存。前面說到對(duì)變量的修改通常都會(huì)先寫入CPU緩存,再寫回內(nèi)存。這就會(huì)出現(xiàn)這樣一種情況,線程1修改了變量A,但此時(shí)修改后的變量A只存儲(chǔ)在CPU緩存中。這時(shí)候線程B去內(nèi)存中讀取變量A,依舊只讀取到舊的值,這就是可見性問題。

線程切換帶來的原子性

為了更充分得利用CPU,引入了CPU時(shí)間片時(shí)間片的概念。進(jìn)程或線程通過爭(zhēng)用CPU時(shí)間片,讓CPU可以更加充分得利用。

比如在進(jìn)行讀寫磁盤等耗時(shí)高的任務(wù)時(shí),就可以將寶貴的CPU資源讓出來讓其他線程去獲取CPU并執(zhí)行任務(wù)。

但這樣的切換也會(huì)導(dǎo)致問題,那就是會(huì)破壞線程某些任務(wù)的原子性。比如java中簡(jiǎn)單的一條語句count += 1。

映射到CPU指令有三條,讀取count變量指令,變量加1指令,變量寫回指令。雖然在高級(jí)語言(java)看來它就是一條指令,但實(shí)際上確是三條CPU指令,并且這三條指令的原子性無法保證。也就是說,可能在執(zhí)行到任意一條指令的時(shí)候被打斷,CPU被其他線程搶占了。而這個(gè)期間變量值可能會(huì)被修改,這里就會(huì)引發(fā)數(shù)據(jù)不一致的情況了。所以高并發(fā)場(chǎng)景下,很多時(shí)候都會(huì)通過鎖實(shí)現(xiàn)原子性。而這個(gè)問題也是很多并發(fā)問題的源頭。

編譯器優(yōu)化帶來的有序性

因?yàn)楝F(xiàn)在程序員編寫的都是高級(jí)語言,編譯器需要將用戶的代碼轉(zhuǎn)成CPU可以執(zhí)行的指令。

同時(shí),由于計(jì)算機(jī)領(lǐng)域的不斷發(fā)展,編譯器也越來越智能,它會(huì)自動(dòng)對(duì)程序員編寫的代碼進(jìn)行優(yōu)化,而優(yōu)化中就有可能出現(xiàn)實(shí)際執(zhí)行代碼順序和編寫的代碼順序不一樣的情況。

而這種破壞程序有序性的行為,在有些時(shí)候會(huì)出現(xiàn)一些非常微妙且難以察覺的并發(fā)編程bug。

舉個(gè)簡(jiǎn)單的例子,我們常見的單例模式是這樣的:

public class Singleton { private Singleton() {} private static Singleton sInstance; public static Singleton getInstance() { if (sInstance == null) {//第一次驗(yàn)證是否為null synchronized (Singleton.class) { //加鎖 if (sInstance == null) { //第二次驗(yàn)證是否為null sInstance = new Singleton(); //創(chuàng)建對(duì)象 } } } return sInstance; }}

即通過兩段判斷加鎖來保證單例的成功生成,但在極小的概率下,可能會(huì)出現(xiàn)異常情況。原因就出現(xiàn)在sInstance = new Singleton();這一行代碼上。這行代碼,我們理解的執(zhí)行順序應(yīng)該是這樣:

為Singleton象分配一個(gè)內(nèi)存空間。 在分配的內(nèi)存空間實(shí)例化對(duì)象。 把Instance 引用地址指向內(nèi)存空間。

但在實(shí)際編譯的過程中,編譯器有可能會(huì)幫我們進(jìn)行優(yōu)化,優(yōu)化完它的順序可能變成如下:

為Singleton對(duì)象分配一個(gè)內(nèi)存空間。 把instance 引用地址指向內(nèi)存空間。 在分配的內(nèi)存空間實(shí)例化對(duì)象。

按照優(yōu)化完的順序,當(dāng)并發(fā)訪問的時(shí)候,可能會(huì)出現(xiàn)這樣的情況

A線程進(jìn)入方法進(jìn)行第1次instance == null判斷。 此時(shí)A線程發(fā)現(xiàn)instance 為null 所以對(duì)Singleton.class加鎖。 然后A線程進(jìn)入方法進(jìn)行第2次instance == null判斷。 然后A線程發(fā)現(xiàn)instance 為null,開始進(jìn)行對(duì)象實(shí)例化。 為對(duì)象分配一個(gè)內(nèi)存空間。 .把Instance 引用地址指向內(nèi)存空間(而就在這個(gè)指令完成后,線程B進(jìn)入了方法)。 B線程首先進(jìn)入方法進(jìn)行第1次instance == null判斷。B線程此時(shí)發(fā)現(xiàn)instance 不為null ,所以它會(huì)直接返回instance (而此時(shí)返回的instance 是A線程還沒有初始化完成的對(duì)象)

最終線程B拿到的instance 是一個(gè)沒有實(shí)例化對(duì)象的空內(nèi)存地址,所以導(dǎo)致instance使用的過程中造成程序錯(cuò)誤。解決辦法很簡(jiǎn)單,可以給sInstance對(duì)象加上一個(gè)關(guān)鍵字,volatile,這樣編譯器就不會(huì)亂優(yōu)化,有關(guān)volatile的具體內(nèi)容后續(xù)再細(xì)說。

主要解決辦法

通過上面的介紹,其實(shí)可以歸納無論是CPU緩存,線程切換還是編譯器優(yōu)化亂序,出現(xiàn)問題的核心都是因?yàn)槎鄠€(gè)線程要并發(fā)讀寫某個(gè)變量或并發(fā)執(zhí)行某段代碼。那么我們可以控制,一次只讓一個(gè)線程執(zhí)行變量讀寫就可以了,這就是互斥。

而在某些時(shí)候,互斥還不夠,還需要一定的條件。比如一個(gè)生產(chǎn)者一個(gè)消費(fèi)者并發(fā),生產(chǎn)者向隊(duì)列存東西,消費(fèi)者向隊(duì)列拿東西。那么生產(chǎn)者寫的時(shí)候要保證存的時(shí)候隊(duì)列不是滿的,消費(fèi)者要保證拿的時(shí)候隊(duì)列非空。這種線程與線程間需要通信協(xié)作的情況,稱為同步,同步可以說是更復(fù)雜的互斥。

既然知道了并發(fā)編程的根源以及同步和互斥,那我們來看看有哪些解決的思路。其實(shí)一共也就三種:

避免共享 Immutability(不變性) 管程及其他工具

下面我們分別說說這三種方案的優(yōu)缺點(diǎn)

避免共享

我們先來說說避免共享,其實(shí)避免共享說是線程本地存儲(chǔ)技術(shù),在java中指的一般就是Threadlocal。ThreadLocal會(huì)為每個(gè)線程提供一個(gè)本地副本,每個(gè)線程都只會(huì)修改自己的ThreadLocal變量。這樣一來就不會(huì)出現(xiàn)共享變量,也就不會(huì)出現(xiàn)沖突了。

其實(shí)現(xiàn)原理是在ThreadLocal內(nèi)部維護(hù)一個(gè)ThreadLocalMap,每次有線程要獲取對(duì)應(yīng)變量的時(shí)候,先獲取當(dāng)前線程,然后根據(jù)不同線程取不同的值,典型的以空間換時(shí)間。

所以ThreadLocal還是比較適用于需要共享資源,且資源占用空間不大的情況。比如一些連接的session啊等等。但是這種模式應(yīng)用場(chǎng)景也較為有限,比如需要同步情況就難以勝任。

Immutability(不變性)

Immutability在函數(shù)式中用得比較多,函數(shù)式編程的一個(gè)主要目的是要寫出無副作用的代碼,有關(guān)什么是無副作用可以參考我以前的文章Scala函數(shù)式編程指南(一) 函數(shù)式思想介紹。而無副作用的一個(gè)主要特點(diǎn)就是變量都是Immutability即不可變的,即創(chuàng)建對(duì)象后不會(huì)再修改對(duì)象,比如scala默認(rèn)的變量和數(shù)據(jù)結(jié)構(gòu)都是不可變的。而在java中,不變性變量即通過final修飾的變量,如String,Long,Double等類型都是Immutability的,它們的內(nèi)部實(shí)現(xiàn)都是基于final關(guān)鍵字的。

那這又和并發(fā)編程有什么關(guān)系呢?其實(shí)啊,并發(fā)問題很大部分原因就是因?yàn)榫€程切換破壞了原子性,這又導(dǎo)致線程隨意對(duì)變量的讀寫破壞了數(shù)據(jù)的一致性。而不變性就不必?fù)?dān)心這個(gè)問題,因?yàn)樽兞慷际遣蛔?,不可寫只能讀的。在這種編程模式下,你要修改一個(gè)變量,那么只能新生成一個(gè)。這樣做的好處很明顯,但壞處也是顯而易見,那就是引入了額外的編程復(fù)雜度,喪失了代碼的可讀性和易用性。

因?yàn)槿绱?,不變性的并發(fā)解決方案其實(shí)相對(duì)而已沒那么廣泛,其中比較有代表性的算是Actor并發(fā)編程模型,我以前也有討論過,有興趣可以看看Actor模型淺析 一致性和隔離性,這種編程模型和常規(guī)并發(fā)解決方案有很顯著的差異。按我的了解,Acctor模式多用在分布式系統(tǒng)的一些協(xié)調(diào)功能,比如維持集群中多個(gè)機(jī)器的心跳通信等等。如果在單機(jī)并發(fā)環(huán)境下,還是下面要介紹的管程類工具才是利器。

管程及其他工具

其實(shí)最早的操作系統(tǒng)中,解決并發(fā)問題用的是信號(hào)量,信號(hào)量通過兩個(gè)原子操作wait(S),和signal(S)(俗稱P,V操作)來實(shí)現(xiàn)訪問資源互斥和同步。比如下面這個(gè)小例子:

//整型信號(hào)量定義int S;//P操作wait(S){ while(S<=0); S--;}//V操作signal(S){ S++;}

雖然信號(hào)量方便有效,但信號(hào)量要對(duì)每個(gè)共享資源都實(shí)現(xiàn)對(duì)應(yīng)的P和V操作,這使得并發(fā)編程中可能要出現(xiàn)大量的P,V操作,并且這部分內(nèi)容難以抽象出來。

為了更好地實(shí)現(xiàn)同步互斥,于是就產(chǎn)生了管程(即Monitor,也有翻譯為監(jiān)視器),值得一提的是,管程也有幾種模型,分別是:Hasen模型,Hoare模型和MESA模型。其中MESA模型應(yīng)用最廣泛,java也是參考自MESA模型。這里簡(jiǎn)單介紹下管程的理論知識(shí),這部分內(nèi)容參考自進(jìn)程同步機(jī)制-----為進(jìn)程并發(fā)執(zhí)行保駕護(hù)航,希望了解更多管程理論知識(shí)的童鞋可以看看。

我們來通過一個(gè)經(jīng)典的生產(chǎn)-消費(fèi)隊(duì)列來解釋,如下圖

JAVA如何解決并發(fā)問題

我們先解釋下圖中右半部分的內(nèi)容,右上角有一個(gè)等待調(diào)用的線程隊(duì)列,管程中每次只能有一個(gè)線程在執(zhí)行任務(wù),所以多個(gè)任務(wù)需要等待。然后是各個(gè)名詞的意思,生產(chǎn)-消費(fèi)需要往隊(duì)列寫入和取出東西,這里的隊(duì)列就是共享變量,對(duì)共享資源進(jìn)行操作稱之為過程(入隊(duì)和出隊(duì)兩個(gè)過程)。而向隊(duì)列寫入和取出是有條件的,寫入的時(shí)候隊(duì)列必須是非滿的,取出的時(shí)候隊(duì)列必須是非空的,這兩個(gè)條件被稱為條件變量。

然后再來看看左半部分的內(nèi)容,假設(shè)線程T1讀取共享變量(即隊(duì)列),此時(shí)發(fā)現(xiàn)隊(duì)列為空(條件變量之一),那么T1此時(shí)需要等待,去哪里等呢?去條件變量隊(duì)列不能為空對(duì)應(yīng)的隊(duì)列中去等待。此時(shí)另一個(gè)線程T2向共享變量隊(duì)列寫數(shù)據(jù),通過了條件變量隊(duì)列不能滿,那么寫完后就會(huì)通知線程T1。但因?yàn)楣艹痰南拗?,管程中只能有一個(gè)線程在執(zhí)行,所以T1線程不能立即執(zhí)行,它會(huì)回到右上角的線程等待隊(duì)列等待(不同的管程模型在這里是有分歧的,比如Hasen模型是立即中斷T2線程讓隊(duì)列中下一個(gè)線程執(zhí)行)。

解釋完這個(gè)圖,管程的概念也就呼之欲出了,

hansen對(duì)管程的定義如下:一個(gè)管程定義了一個(gè)數(shù)據(jù)結(jié)構(gòu)和能力為并發(fā)進(jìn)程所執(zhí)行(在該數(shù)據(jù)結(jié)構(gòu)上)的一組操作,這組操作能同步進(jìn)程和改變管程中的數(shù)據(jù)。

本質(zhì)上,管程是對(duì)共享資源以及對(duì)共享資源的操作抽象成變量和方法,要操作共享變量?jī)H能通過管程提供的方法(比如上面的入隊(duì)和出隊(duì))間接訪問。所以你會(huì)發(fā)現(xiàn)管程其實(shí)和面向?qū)ο蟮睦砟钍鞘窒嘟模趈ava中,主要提供了低層次了synchronized關(guān)鍵字和wait(),notify()等方法。同時(shí)還提供了高層次的ReenTrantLock和Condition來實(shí)現(xiàn)管程模型。

以上就是JAVA如何解決并發(fā)問題的詳細(xì)內(nèi)容,更多關(guān)于JAVA 并發(fā)的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Java
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
视频一区二区三区入口| 日韩高清欧美| 亚洲欧美网站| 亚洲专区一区| 欧美专区18| 亚洲天堂av资源在线观看| 日本大胆欧美人术艺术动态| 老司机精品久久| 亚洲香蕉视频| 久久成人精品| 亚洲影院天堂中文av色| 午夜性色一区二区三区免费视频| 日韩精品亚洲一区二区三区免费| 911精品国产| 久久精品国产福利| 久久电影tv| 欧美精品一区二区久久| 亚洲视频播放| 日本不卡在线视频| 久久不卡国产精品一区二区| 精品美女在线视频| 欧美成a人国产精品高清乱码在线观看片在线观看久| 中文字幕在线高清| 在线亚洲免费| 日韩美女精品| 老司机免费视频一区二区| 国内精品美女在线观看| 欧洲精品一区二区三区| 婷婷丁香综合| 日本91福利区| 精品国产网站| 亚洲欧美一区在线| 综合亚洲视频| 老司机免费视频一区二区三区| 婷婷激情一区| 蜜臀av性久久久久蜜臀aⅴ流畅| 日本不卡视频在线| 国内精品美女在线观看| 亚洲福利国产| 蜜桃久久久久久| 国产精品115| 精品中文一区| 国产欧美日韩精品高清二区综合区| 久久久久久色 | 国产传媒在线观看| 成人啊v在线| 蜜臀精品久久久久久蜜臀| 国产精品亚洲欧美| 欧美不卡视频| 国产日韩视频| 好吊日精品视频| 欧美激情福利| 亚洲一区二区网站| 国产一区二区三区不卡视频网站 | 91精品亚洲| 色综合视频一区二区三区日韩| 精品亚洲精品| 亚洲欧美日韩综合国产aⅴ| 国产精品激情电影| 妖精视频成人观看www| 国产精品4hu.www| 亚洲制服少妇| 久久久久久久欧美精品| 久久久国产精品一区二区中文| 亚洲美女91| 国产亚洲一区二区手机在线观看 | 嫩草伊人久久精品少妇av杨幂 | 91在线成人| 黑丝美女一区二区| 免费一级欧美片在线观看网站 | 成人午夜在线| 亚洲精品裸体| 成人久久久久| 国产精品亚洲二区| 亚洲中午字幕| 国产麻豆久久| 久久免费精品| 91亚洲精品视频在线观看 | 亚洲人成亚洲精品| 91精品蜜臀一区二区三区在线| 国产欧美自拍| 亚洲综合色婷婷在线观看| 性欧美videohd高精| 国产欧美日本| 四虎在线精品| 久久电影一区| 成人啊v在线| 国产一区2区在线观看| 欧美亚洲三区| 天堂成人国产精品一区| 久久久水蜜桃av免费网站| 麻豆中文一区二区| 日韩黄色在线观看| 免费在线观看成人| 国产一区日韩一区| 亚洲女同av| 国产成人免费av一区二区午夜| 亚洲视频二区| 久久先锋影音| 最新亚洲激情| 91精品99| 精精国产xxxx视频在线播放 | 午夜久久久久| 99成人在线视频| 日本在线高清| 国产精品久久久久蜜臀| 美腿丝袜亚洲三区| 国产精品sss在线观看av| 97久久超碰| 日韩高清中文字幕一区| 天堂va欧美ⅴa亚洲va一国产| 亚洲伊人影院| 蜜臀91精品一区二区三区| 亚洲午夜av| 国产99精品一区| 免费久久精品| 亚洲激情偷拍| 水蜜桃久久夜色精品一区的特点| 好吊一区二区三区| 91久久午夜| 蘑菇福利视频一区播放| 老牛国产精品一区的观看方式| 国产精品丝袜xxxxxxx| 99视频精品| 欧美一级专区| 亚洲人亚洲人色久| 日韩精品第二页| 日韩高清欧美激情| 欧美日韩精品一区二区三区视频| 日韩久久一区| 国产精品一区毛片| 欧美国产视频| 精品视频在线一区二区在线| 精品视频网站| 国产理论在线| 欧美日韩在线网站| 亚洲一区二区三区高清| 综合国产精品| 国产精品久久久久久久久久白浆 | 69堂精品视频在线播放| 欧美一级全黄| 久久中文字幕一区二区| 在线天堂中文资源最新版| 久久久久蜜桃| 日韩精品一级中文字幕精品视频免费观看 | 视频一区国产视频| 日韩在线观看一区二区三区| 日本视频中文字幕一区二区三区| 国产精品视频一区二区三区 | av一区在线| 99亚洲精品| 日韩精品亚洲专区| 久久gogo国模啪啪裸体| 人在线成免费视频| 在线一区免费观看| 色婷婷成人网| 精品视频国产| 欧美亚洲在线日韩| 中文字幕一区二区精品区| 欧美日韩一区二区三区四区在线观看| 精品久久网站| 99re国产精品| 国产精品1luya在线播放| 午夜精品久久久久久久久久蜜桃| 91久久久精品国产| 欧美日韩一区自拍| 久久久精品午夜少妇| 影音先锋久久精品| 红杏一区二区三区| 国产视频一区三区| 欧美日韩亚洲国产精品| 亚洲欧洲高清| 四虎精品永久免费| 97精品一区| 色8久久久久| 日韩av免费| 欧美视频久久| 91国语精品自产拍| 国产精品igao视频网网址不卡日韩| 成人羞羞视频在线看网址| 视频一区视频二区中文字幕| 国产精品2023| 午夜国产一区二区| 国产精品午夜av| 亚洲天堂黄色| 青青伊人久久| 成人一二三区| 久久亚洲视频| 久久精品国产成人一区二区三区| 欧美日韩国产欧| 精品网站aaa| 亚洲麻豆一区| 久久免费黄色| 久久精品色播| 亚洲精品系列| 999久久久91| 国产精品久久久久久久久免费高清| 久久久国产亚洲精品| 欧美精品观看| 在线综合亚洲|