溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

詳談鎖和監(jiān)視器之間的區(qū)別_Java并發(fā)

發(fā)布時(shí)間:2020-09-05 17:13:05 來源:腳本之家 閱讀:149 作者:jingxian 欄目:編程語言

在面試中你可能遇到過這樣的問題:鎖(lock)和監(jiān)視器(monitor)有什么區(qū)別?

嗯,要回答這個(gè)問題,你必須深入理解Java的多線程底層是如何工作的。

簡(jiǎn)短的答案是,鎖為實(shí)現(xiàn)監(jiān)視器提供必要的支持。詳細(xì)答案如下。

鎖(lock)

邏輯上鎖是對(duì)象內(nèi)存堆中頭部的一部分?jǐn)?shù)據(jù)。JVM中的每個(gè)對(duì)象都有一個(gè)鎖(或互斥鎖),任何程序都可以使用它來協(xié)調(diào)對(duì)對(duì)象的多線程訪問。如果任何線程想要訪問該對(duì)象的實(shí)例變量,那么線程必須擁有該對(duì)象的鎖(在鎖內(nèi)存區(qū)域設(shè)置一些標(biāo)志)。所有其他的線程試圖訪問該對(duì)象的變量必須等到擁有該對(duì)象的鎖有的線程釋放鎖(改變標(biāo)記)。

一旦線程擁有一個(gè)鎖,它可以多次請(qǐng)求相同的鎖,但是在其他線程能夠使用這個(gè)對(duì)象之前必須釋放相同數(shù)量的鎖。如果一個(gè)線程請(qǐng)求一個(gè)對(duì)象的鎖三次,如果別的線程想擁有該對(duì)象的鎖,那么之前線程需要 “釋放”三次鎖。

Java中顯示鎖的使用語法如下:

…
private Lock bankLock = new ReentrantLock();
…
 public double getTotalBalance()
  {
   bankLock.lock();
   try
   {
     double sum = 0;

     for (double a : accounts)
      sum += a;

     return sum;
   }
   finally
   {
     bankLock.unlock();
   }
  }

1) 鎖用來保護(hù)代碼片段,任何時(shí)刻只能有一個(gè)線程執(zhí)行被保護(hù)的代碼。

2) 鎖可以管理試圖進(jìn)入被保護(hù)代碼的線程

3) 鎖可以擁有一個(gè)或者多個(gè)相關(guān)的條件對(duì)象

4) 每個(gè)條件對(duì)象管理那些已經(jīng)進(jìn)入被保護(hù)的代碼段,但還不能運(yùn)行的線程

Lock和Condition接口為程序設(shè)計(jì)人員提供了高度的鎖定控制。然后大多數(shù)情況下,并不需要這樣的控制,并且可以使用一種嵌入Java語言的內(nèi)部機(jī)制。從1.0版本開始,Java中的每一個(gè)對(duì)象都有一個(gè)內(nèi)部鎖。如果一個(gè)方法用synchronized關(guān)鍵字聲明,那么對(duì)象的鎖將保護(hù)整個(gè)方法。也就說,要調(diào)用該方法,線程必須獲得內(nèi)部的對(duì)象鎖。

內(nèi)部鎖的一般用法如下:

public synchronized void transfer(int from, int to, double amount) throws InterruptedException
  {
   while (accounts[from] < amount)
     wait();
   System.out.print(Thread.currentThread());
   accounts[from] -= amount;
   System.out.printf(" %10.2f from %d to %d", amount, from, to);
   accounts[to] += amount;
   System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
   notifyAll();
  }

可以看到,用synchronized關(guān)鍵字來編寫代碼簡(jiǎn)潔得多。當(dāng)然要理解這一代碼,你必須了解每一個(gè)對(duì)象有一個(gè)內(nèi)部鎖,并且該鎖有一個(gè)內(nèi)部條件。由鎖來管理那些試圖進(jìn)入synchronized方法的線程,由條件來管理那些調(diào)用wait的線程

然而,講了這么多,實(shí)際上推薦最好優(yōu)先使用BlockQueue,Excutor,同步集合等,然后再是synchronized關(guān)鍵字,最才是Lock/Condition

監(jiān)視器(Monitor)

監(jiān)視器是一中同步結(jié)構(gòu),它允許線程同時(shí)互斥(使用鎖)和協(xié)作,即使用等待集(wait-set)使線程等待某些條件為真的能力。

互斥

使用比較形象的說明,監(jiān)視器就像一個(gè)包含一個(gè)特殊房間(對(duì)象實(shí)例)的建筑物,每次只能占用一個(gè)線程。這個(gè)房間通常包含一些需要防止并發(fā)訪問的數(shù)據(jù)。從一個(gè)線程進(jìn)入這個(gè)房間到它離開的時(shí)間,它可以獨(dú)占訪問房間中的任何數(shù)據(jù)。進(jìn)入監(jiān)控的建筑被稱為“進(jìn)入監(jiān)控監(jiān)視器。”進(jìn)入建筑內(nèi)部特殊的房間叫做“獲取監(jiān)視器”。房間占領(lǐng)被稱為“擁有監(jiān)視器”,離開房間被稱為“釋放監(jiān)視器?!弊屨麄€(gè)建筑被稱為“退出監(jiān)視器?!?/p>

當(dāng)一個(gè)線程訪問受保護(hù)的數(shù)據(jù)(進(jìn)入特殊的房間)時(shí),它首先在建筑物接收(entry-set)中排隊(duì)。如果沒有其他線程在等待(擁有監(jiān)視器),線程獲取鎖并繼續(xù)執(zhí)行受保護(hù)的代碼。當(dāng)線程完成執(zhí)行時(shí),它釋放鎖并退出大樓(退出監(jiān)視器)。

如果當(dāng)一個(gè)線程到達(dá)并且另一個(gè)線程已經(jīng)擁有監(jiān)視器時(shí),它必須在接收隊(duì)列中等待(entry-set)。當(dāng)當(dāng)前所有者退出監(jiān)視器時(shí),新到達(dá)的線程必須與在入口集中等待的其他線程競(jìng)爭(zhēng)。只有一個(gè)線程能贏得競(jìng)爭(zhēng)并擁有鎖。

這里沒有wait-set的事。

合作

一般來說,只有當(dāng)多個(gè)線程共享數(shù)據(jù)或其他資源時(shí),互斥才是重要的。如果兩個(gè)線程不處理任何公共數(shù)據(jù)或資源,它們通常不能互相干擾,也不需要以互斥的方式執(zhí)行。盡管互斥有助于防止線程在共享數(shù)據(jù)時(shí)互相干擾,但合作有助于線程共同努力實(shí)現(xiàn)一些共同目標(biāo)。

合作在當(dāng)一個(gè)線程需要的數(shù)據(jù)改變?yōu)樵谝粋€(gè)特定的狀態(tài)時(shí)很重要,另一個(gè)線程負(fù)責(zé)將數(shù)據(jù)該入狀態(tài),如生產(chǎn)者/消費(fèi)者的問題,讀線程需要緩沖去在一個(gè)“不空”的狀態(tài)才可以從緩沖區(qū)中讀取任何數(shù)據(jù)。如果讀線程發(fā)現(xiàn)緩沖區(qū)為空,則必須等待。寫線程負(fù)責(zé)用數(shù)據(jù)填充緩沖區(qū)。一旦寫入線程完成了更多的寫入操作,讀線程可以進(jìn)行更多的讀取。它有時(shí)也稱為“Wait and Notify”或“Signal and Continue”監(jiān)視器,因?yàn)樗A魧?duì)監(jiān)視器的所有權(quán),并繼續(xù)執(zhí)行監(jiān)視區(qū)域(如果需要的話繼續(xù))。在稍后的時(shí)間內(nèi),通知線程釋放監(jiān)視器,等待線程重新恢復(fù)擁有鎖。

這種合作需要entry-setwait-set.。下面的圖表將有助于您理解這種合作。

詳談鎖和監(jiān)視器之間的區(qū)別_Java并發(fā)

上圖顯示監(jiān)視器為三個(gè)矩形。在該中心,一個(gè)大矩形包含一個(gè)線程,即監(jiān)視器的所有者。在左邊,一個(gè)小矩形包含entry set。在右邊,另一個(gè)小矩形包含wait set。

監(jiān)視器是由Per Brich Hansen和Tony Hoare提出的概念,Java以不精確的方式采用了它,也就是Java中的每個(gè)對(duì)象有一個(gè)內(nèi)部的鎖和內(nèi)部條件。如果一個(gè)方法用synchronized關(guān)鍵字聲明,那么,它表現(xiàn)的就像一個(gè)監(jiān)視器方法。通過wait/notifyAll/nofify來訪問條件變量

我希望上面內(nèi)容將有助于你更深入地了解Java多線程,歡迎提出任何問題。

以上這篇詳談鎖和監(jiān)視器之間的區(qū)別_Java并發(fā)就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持億速云。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI