最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Java中多線程同步問題、生產(chǎn)者與消費(fèi)者、守護(hù)線程和volatile關(guān)鍵字(附帶相關(guān)面試題)

2023-08-06 07:13 作者:Alphamilk  | 我要投稿

1.多線程同步問題(關(guān)鍵字Synchronized)

問題:多線程訪問同一個資源時候可能就會出現(xiàn)資源完整性的問題

所以引入關(guān)鍵字synchronized(同步)

  • synchronized關(guān)鍵字的作用機(jī)制是給對象加鎖,并為每個線程提供了一個計數(shù)器,初始值為0。當(dāng)?shù)谝粋€線程獲得鎖時,計數(shù)器變?yōu)?,其他線程被阻塞。當(dāng)?shù)谝粋€線程執(zhí)行完代碼并釋放鎖時,計數(shù)器歸零,意味著資源可用,所有被阻塞的線程將恢復(fù)執(zhí)行。

  • 一個通俗的比喻是廁所的使用情況。假設(shè)只有一個廁所位置但有很多人需要使用。當(dāng)?shù)谝粋€人進(jìn)入廁所并鎖上門時,其他人不得不在外面等待。當(dāng)?shù)谝粋€人使用完畢并打開門鎖時,表示廁所空閑可用,所有等待的人可以繼續(xù)使用。

關(guān)于synchronized有兩種用法

1.設(shè)置同步代碼塊

意味著只有同步代碼塊內(nèi)部的代碼需要同步,其他操作無需同步

2.設(shè)置同步代碼方法

這個方法就是整個方法內(nèi)的代碼都是同步的

注意:在生產(chǎn)案例中不要隨意使用同步方法,因為一旦同步,整個程序的運(yùn)行效率就會非常低,比如10個學(xué)生想要去學(xué)校上廁所,那么最好的操作就是讓10個學(xué)生一起先到學(xué)校再同步操作上廁所,而不是10個學(xué)生其中某一個去學(xué)校上完廁所,其他學(xué)生才去學(xué)校上廁所

關(guān)于同步案例:(多個售票員售賣固定數(shù)量的票)

在上一篇文章的代碼中就實(shí)現(xiàn)到這一步

這一步實(shí)現(xiàn)了售票員之間的售賣間隔,就不會一下子就把所有票賣光。

現(xiàn)在通過同步代碼塊方法實(shí)現(xiàn)同步:

由于票太多所以為了方便顯示就改成5了

面試題: Synchronized 用過嗎,其原理是什么?

(1)可重入性

synchronized的鎖對象中有一個計數(shù)器(recursions變量)會記錄線程獲得幾次鎖;

??? 可重入的好處:
??? 可以避免死鎖;
??? 可以讓我們更好的封裝代碼;

synchronized是可重入鎖,每部鎖對象會有一個計數(shù)器記錄線程獲取幾次鎖,在執(zhí)行完同步代碼塊時,計數(shù)器的數(shù)量會-1,直到計數(shù)器的數(shù)量為0,就釋放這個鎖。

(2)不可中斷性

??? 一個線程獲得鎖后,另一個線程想要獲得鎖,必須處于阻塞或等待狀態(tài),如果第一個線程不釋放鎖,第二個線程會一直阻塞或等待,不可被中斷;
??? synchronized 屬于不可被中斷;
??? Lock lock方法是不可中斷的;
??? Lock tryLock方法是可中斷的;

?面試題:為什么說 Synchronized 是非公平鎖?

當(dāng)鎖被釋放后,任何一個線程都有機(jī)會競爭得到鎖,這樣做的目的是提高效率,但缺點(diǎn)是可能產(chǎn)生線程饑餓現(xiàn)象。

面試題:為什么說 Synchronized 是一個悲觀鎖?樂觀鎖的實(shí)現(xiàn)原理又是什么?什么是 CAS,它有什么特性?

Synchronized的并發(fā)策略是悲觀的,不管是否產(chǎn)生競爭,任何數(shù)據(jù)的操作都必須加鎖。

樂觀鎖的核心是CAS,CAS包括內(nèi)存值、預(yù)期值、新值,只有當(dāng)內(nèi)存值等于預(yù)期值時,才會將內(nèi)存值修改為新值。

2.?Object線程的等待與喚醒方法

注意:

這些方法必須在同步塊或同步方法中使用,因為它們會改變對象的內(nèi)部鎖狀態(tài)。調(diào)用wait()方法將釋放當(dāng)前線程持有的對象鎖,并使線程進(jìn)入等待狀態(tài)。而調(diào)用notify()notifyAll()方法會喚醒等待在該對象上的線程,并將其重新放入可運(yùn)行狀態(tài)。

案例要求:設(shè)置一個圖書類,一個圖書管理員可以放圖書的書名和作者,一個讀者可以看圖書的書名和作者

?在網(wǎng)絡(luò)延遲的情況下可能會出現(xiàn)問題:

1.數(shù)據(jù)不匹配(解決需要-》結(jié)果能夠一一對應(yīng))

2.重復(fù)取同一個數(shù)據(jù)(解決需要--》每次都只取一次,只有更改后再?。?/p>

解決方法:

1.數(shù)據(jù)錯亂,根本原因在于多線程下,圖書管理員線程在設(shè)置圖書信息到一半的時候,讀者就讀取圖書信息造成圖書信息錯亂,解決方法很簡單,只需要在book下將所有g(shù)et和set方法設(shè)置為同步代碼方法就可以解決數(shù)據(jù)錯亂了

2.其原理也很簡單就是因為同步代碼塊,所以在完成Manger在執(zhí)行完set前不會執(zhí)行g(shù)et。Reader在執(zhí)行完get前也不會執(zhí)行set


雖然解決了數(shù)據(jù)錯亂的問題但是這樣的數(shù)據(jù)出現(xiàn)有重復(fù),我們的目標(biāo)是需要西游記后輸出天龍八部的交替輸出。這就是最基礎(chǔ)的生產(chǎn)者消費(fèi)者模型了。

那么如何具體實(shí)現(xiàn)呢?就需要用到前面給的方法,等待喚醒機(jī)制,即wait();與notify();

其工作原理是設(shè)置一個標(biāo)志位(資源量)當(dāng)一個Reader讀取時候就設(shè)置為true,maneger就是false并且進(jìn)入沉睡。當(dāng)Reader執(zhí)行完任務(wù)后又將標(biāo)志位設(shè)置為false 并讓執(zhí)行notify()喚醒其他熟睡的線程

案例實(shí)現(xiàn)代碼:


面試題: Java 如何實(shí)現(xiàn)多線程之間的通訊和協(xié)作?

1.可以通過中斷 和 共享變量的方式實(shí)現(xiàn)線程間的通訊和協(xié)作

比如說最經(jīng)典的生產(chǎn)者-消費(fèi)者模型:當(dāng)隊列滿時,生產(chǎn)者需要等待隊列有空間才能繼續(xù)往里面放入商品,而在等待的期間內(nèi),生產(chǎn)者必須釋放對臨界資源(即隊列)的占用權(quán)。因為生產(chǎn)者如果不釋放對臨界資源的占用權(quán),那么消費(fèi)者就無法消費(fèi)隊列中的商品,就不會讓隊列有空間,那么生產(chǎn)者就會一直無限等待下去。因此,一般情況下,當(dāng)隊列滿時,會讓生產(chǎn)者交出對臨界資源的占用權(quán),并進(jìn)入掛起狀態(tài)。然后等待消費(fèi)者消費(fèi)了商品,然后消費(fèi)者通知生產(chǎn)者隊列有空間了。同樣地,當(dāng)隊列空時,消費(fèi)者也必須等待,等待生產(chǎn)者通知它隊列中有商品了。這種互相通信的過程就是線程間的協(xié)作。


Java中線程通信協(xié)作的最常見的兩種方式:

1、syncrhoized加鎖的線程的Object類的wait()/notify()/notifyAll()

2、ReentrantLock類加鎖的線程的Condition類的await()/signal()/signalAll()

線程間直接的數(shù)據(jù)交換:

通過管道進(jìn)行線程間通信:1)字節(jié)流;2)字符流

?3.模擬生產(chǎn)者與消費(fèi)者

模擬生產(chǎn)者與消費(fèi)者

通過上面一個案例應(yīng)該就有對生產(chǎn)者消費(fèi)者模型有初步了解,下面將舉一個十分經(jīng)典的消費(fèi)者生產(chǎn)者模型讓大家有更深層次的理解

案例目標(biāo):設(shè)計一個生產(chǎn)計算機(jī)類與一個搬運(yùn)計算機(jī)類,要求是生產(chǎn)者生產(chǎn)一臺計算機(jī)就要搬走一臺計算機(jī),如果沒有新的計算機(jī)那么搬運(yùn)工就要等待新的計算機(jī)產(chǎn)出,如果生產(chǎn)出的計算機(jī)沒有被搬走就要等待搬運(yùn)者將計算機(jī)搬走,最后搬運(yùn)統(tǒng)計搬運(yùn)走的電腦個數(shù)

案例代碼:


4.守護(hù)線程

一個進(jìn)程的運(yùn)行往往可能需要十分多的子進(jìn)程輔助運(yùn)行,比如聊天軟件,主線程是軟件的使用。而所有的聊天對象都是子線程可以分別接收消息。當(dāng)軟件關(guān)閉主線程時候即關(guān)閉軟件使用時候,此時子線程的存在就沒有了意義。就會自動關(guān)閉。這樣的子線程就叫做守護(hù)線程

設(shè)置守護(hù)線程

Thread 對象.setDeamon(true/false);true-》開啟守護(hù)線程,false-關(guān)閉守護(hù)線程

具體應(yīng)用案例:



5.volatile關(guān)鍵字

一般情況下比如之前的售票員售票其調(diào)用ticket時候是進(jìn)行先復(fù)制其數(shù)據(jù)副本再通過加載-》使用-》賦值-》存儲-》寫入才對內(nèi)存的數(shù)據(jù)ticket進(jìn)行同步,就是線程操作的數(shù)據(jù)都只是原始數(shù)據(jù)的備份,在操作完成后再和原始數(shù)據(jù)進(jìn)行替換。而volatile則不需要這些數(shù)據(jù)備份直接操作內(nèi)存的原始數(shù)據(jù)

好處:volatile關(guān)鍵字可以直接對內(nèi)存進(jìn)行操作,就不需要同步數(shù)據(jù)了,所以可以減少程序運(yùn)行的時間

使用案例代碼:

*面試題:volatile 關(guān)鍵字的作用

對于可見性,Java 提供了 volatile 關(guān)鍵字來保證可見性和禁止指令重排。 volatile 提供 happens-before 的保證,確保一個線程的修改能對其他線程是可見的。當(dāng)一個共享變量被 volatile 修飾時,它會保證修改的值會立即被更新到主存,當(dāng)有其他線程需要讀取時,它會去內(nèi)存中讀取新值。

從實(shí)踐角度而言,volatile 的一個重要作用就是和 CAS 結(jié)合,保證了原子性,詳細(xì)的可以參見 java.util.concurrent.atomic 包下的類,比如 AtomicInteger。

volatile 常用于多線程環(huán)境下的單次操作(單次讀或者單次寫)。

?*面試題:既然 volatile 能夠保證線程間的變量可見性,是不是就意味著基于 volatile 變量的運(yùn)算就是并發(fā)安全的?

volatile修飾的變量在各個線程的工作內(nèi)存中不存在一致性的問題(在各個線程工作的內(nèi)存中,volatile修飾的變量也會存在不一致的情況,但是由于每次使用之前都會先刷新主存中的數(shù)據(jù)到工作內(nèi)存,執(zhí)行引擎看不到不一致的情況,因此可以認(rèn)為不存在不一致的問題),但是java的運(yùn)算并非原子性的操作,導(dǎo)致volatile在并發(fā)下并非是線程安全的。

??*面試題:請談?wù)?volatile 有什么特點(diǎn),為什么它能保證變量對所有線程的可見性?

volatile只能作用于變量,保證了操作可見性和有序性,不保證原子性。

在Java的內(nèi)存模型中分為主內(nèi)存和工作內(nèi)存,Java內(nèi)存模型規(guī)定所有的變量存儲在主內(nèi)存中,每條線程都有自己的工作內(nèi)存。

主內(nèi)存和工作內(nèi)存之間的交互分為8個原子操作:

??? lock
??? unlock
??? read
??? load
??? assign
??? use
??? store
??? write

volatile修飾的變量,只有對volatile進(jìn)行assign操作,才可以load,只有l(wèi)oad才可以use,,這樣就保證了在工作內(nèi)存操作volatile變量,都會同步到主內(nèi)存中。


Java中多線程同步問題、生產(chǎn)者與消費(fèi)者、守護(hù)線程和volatile關(guān)鍵字(附帶相關(guān)面試題)的評論 (共 條)

分享到微博請遵守國家法律
蒲城县| 临清市| 平舆县| 韶山市| 隆化县| 江永县| 高雄市| 彭阳县| 宝鸡市| 甘洛县| 灯塔市| 承德市| 陆良县| 镇康县| 西林县| 垦利县| 石林| 简阳市| 安化县| 嘉定区| 阿瓦提县| 太仆寺旗| 昭苏县| 陆丰市| 新沂市| 康乐县| 邵阳县| 永平县| 全南县| 舟曲县| 巩义市| 吉木萨尔县| 梅河口市| 措美县| 广丰县| 富宁县| 土默特左旗| 扎囊县| 平昌县| 确山县| 八宿县|