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

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

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

瀏覽:200日期: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)樽兞慷际遣蛔儯豢蓪懼荒茏x的。在這種編程模式下,你要修改一個(gè)變量,那么只能新生成一個(gè)。這樣做的好處很明顯,但壞處也是顯而易見,那就是引入了額外的編程復(fù)雜度,喪失了代碼的可讀性和易用性。

因?yàn)槿绱耍蛔冃缘牟l(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久久,三上悠亚国产精品一区二区三区
综合日韩在线| 成人亚洲一区| 影音先锋国产精品| 欧美+日本+国产+在线a∨观看| 美女av在线免费看| 亚洲va中文在线播放免费| 欧美精选视频一区二区| 亚洲手机视频| 香蕉精品999视频一区二区| 男女激情视频一区| 日韩高清一级| 国产精品极品| 久久久男人天堂| 在线手机中文字幕| 久久精品影视| 在线亚洲精品| 日本v片在线高清不卡在线观看| 91九色综合| 国产在线不卡一区二区三区| 日韩av一级| 久色成人在线| 久久黄色影视| 成人黄色av| 欧美另类综合| 日本不卡高清| 国产精品不卡| 99xxxx成人网| 日本欧美在线| 精品理论电影在线| 亚洲二区在线| 日韩高清电影一区| 在线中文字幕播放| 免费精品视频最新在线| 国产精选久久| 精品日韩视频| 亚洲小说春色综合另类电影| 国产精品亚洲综合在线观看| 欧美一级鲁丝片| 国产精品毛片在线看| 国产欧美日韩在线一区二区| 色婷婷色综合| 久久午夜视频| 精品一区二区三区免费看 | 午夜久久黄色| 日韩av字幕| 日韩在线高清| 亚洲另类视频| 福利在线免费视频| 亚洲伊人精品酒店| 国产精品xx| 亚洲欧美日本日韩| 久久久精品区| 亚洲一区激情| 精品国产美女a久久9999| 伊人久久婷婷| 深夜福利亚洲| 日韩一区电影| 日韩国产欧美一区二区三区| 久久激情网站| 国产美女亚洲精品7777| 欧美日韩一区二区综合| 欧美性www| 亚洲伦乱视频| 国产欧美在线| 天堂成人免费av电影一区 | 欧美1区2区3| 乱人伦精品视频在线观看| 国产 日韩 欧美 综合 一区| 日韩中文字幕1| 国内精品伊人| 日韩精品一区二区三区中文在线| 日韩成人亚洲| 久久91视频| 亚洲三级网址| 亚洲二区精品| 欧美激情另类| 国产毛片精品久久| 亚洲最新av| 免费精品国产的网站免费观看| 麻豆中文一区二区| 日韩欧美高清一区二区三区| 一区二区自拍| 秋霞影院一区二区三区| 免费一级欧美片在线观看网站| 国产精品女主播一区二区三区| 国产a亚洲精品| 欧美日韩99| 日韩精品一区第一页| 日韩国产一区二区三区| 国产精品自拍区| 日韩在线成人| 亚洲欧美日韩精品一区二区| 久久精品123| 日韩精品电影| 日韩av自拍| 老司机免费视频一区二区三区| 亚洲精品第一| 中文字幕日本一区| 国产亚洲网站| 91精品国产乱码久久久久久久| 欧美aa在线视频| 国产欧美日韩免费观看| 日欧美一区二区| 蜜桃久久av| 午夜在线精品偷拍| av不卡免费看| 午夜精品影院| 亚洲激情社区| 亚洲精品电影| 亚洲五月婷婷| 欧美日韩在线播放视频| 天堂日韩电影| 亚洲不卡av不卡一区二区| 国产91在线精品| 麻豆91在线播放| 欧美激情网址| 国产日本久久| 国产欧美日韩一区二区三区四区| 亚洲精品动态| 五月国产精品| 日韩激情精品| 911亚洲精品| 国产欧美激情| 精品一区av| 美女一区网站| 久久精品高清| 欧美日韩国产高清电影| av亚洲免费| 亚洲黄色影院| 国产精品婷婷| 亚洲日本在线观看视频| 日韩精品一区二区三区中文字幕| 日韩三区四区| 国产欧美亚洲一区| 精品国产网站| 99久久婷婷这里只有精品| 蜜臀91精品国产高清在线观看| 国产亚洲精品v| 亚洲乱码久久| 久久不见久久见免费视频7| 狠狠久久伊人| 久久国产毛片| 只有精品亚洲| 国产精品伊人| 成人在线观看免费视频| 另类中文字幕国产精品| 一区二区视频欧美| 日本亚洲欧美天堂免费| 国产精品久久国产愉拍| 一区二区三区四区日本视频| 国产一区亚洲| 中文字幕一区二区三区在线视频| 欧美日韩国产一区二区在线观看| 欧美精品第一区| 午夜精品成人av| 久久亚洲美女| 欧美黄色精品| 国产精品av久久久久久麻豆网| 亚洲一区二区日韩| 97久久中文字幕| 麻豆高清免费国产一区| 久久久久久久久久久9不雅视频| 日韩一区精品字幕| 久久中文字幕一区二区三区| 欧美日中文字幕| 日韩在线成人| 免费一二一二在线视频| 视频一区视频二区中文字幕| 久久国产三级| 国产精品久久久久av电视剧| 男人的天堂久久精品| 久久99偷拍| 在线精品小视频| 国产精品一区三区在线观看| 国产精品黑丝在线播放| 免费视频久久| 精品三级av| 久久国产高清| 黄色网一区二区| 麻豆久久精品| 成人国产精选| 在线精品亚洲| 亚洲成人一区在线观看| 欧美一区久久| 视频一区中文| 精品一区二区三区中文字幕| 久久高清一区| 激情视频网站在线播放色| 中文字幕中文字幕精品| 国产精品久久久久久久久妇女| 中文一区一区三区免费在线观| 亚洲黄色免费av| 天堂俺去俺来也www久久婷婷| 日韩深夜视频| 日本欧美韩国一区三区| 欧美日韩在线二区| 美腿丝袜亚洲一区| 综合激情一区| 亚洲二区免费| 国产精品麻豆久久|