久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

JVM 進行線程同步背后的原理

 spzproot 2017-04-14

前言

所有的 Java 程序都會被翻譯為包含字節(jié)碼的 class 文件,字節(jié)碼是 JVM 的機器語言,。這篇文章將闡述 JVM 是如何處理線程同步以及相關(guān)的字節(jié)碼,。

線程和共享數(shù)據(jù)

Java 的一個優(yōu)點就是在語言層面支持多線程,這種支持集中在協(xié)調(diào)多線程對數(shù)據(jù)的訪問上,。

JVM 將運行時數(shù)據(jù)劃分為幾個區(qū)域:一個或多個棧,,一個堆,一個方法區(qū),。

在 JVM 中,,每個線程擁有一個棧,其他線程無法訪問,,里面的數(shù)據(jù)包括:局部變量,,函數(shù)參數(shù),線程調(diào)用的方法的返回值,。棧里面的數(shù)據(jù)只包含原生數(shù)據(jù)類型和對象引用,。在 JVM 中,,不可能將實際對象的拷貝放入棧。所有對象都在堆里面,。

JVM 只有一個堆,,所有線程都共享它。堆中只包含對象,,把單獨的原生類型或者對象引用放入堆也是不可能的,,除非它們是對象的一部分。數(shù)組也在堆中,,包括原生類型的數(shù)組,,因為在 Java 中,數(shù)組也是對象,。

除了棧和堆,,另一個存放數(shù)據(jù)的區(qū)域就是方法區(qū)了,它包含程序中使用到的所有類(靜態(tài))變量,。方法區(qū)類似于棧,,也只包含原生類型和對象引用,但是又跟棧不同,,方法區(qū)中類變量是線程共享的,。

對象鎖和類鎖

正如前面所說,JVM 中的兩個區(qū)域包含線程共享的數(shù)據(jù),,分別是:

  1. :包含所有對象
  2. 方法區(qū):包含所有類變量

如果多個線程需要同時使用同一個對象或者類變量,,它們對數(shù)據(jù)的訪問必須被恰當?shù)乜刂啤7駝t,,程序會產(chǎn)生不可預(yù)測的行為,。

為了協(xié)調(diào)多個線程對共享數(shù)據(jù)的訪問,JVM 給每個對象和類關(guān)聯(lián)了一個鎖,。鎖就像是任意時間點只有一個線程能夠擁有的特權(quán),。如果一個線程想要鎖住一個特定的對象或者類,它需要向 JVM 請求鎖,。線程向 JVM 請求鎖之后,可能很快就拿到,,或者過一會就拿到,,也可能永遠拿不到。當線程不需要鎖之后,,它把鎖還給 JVM,。如果其他線程需要這個鎖,JVM 會交給該線程,。

類鎖的實現(xiàn)其實跟對象鎖是一樣的,。當 JVM 加載類文件的時候,,它會創(chuàng)建一個對應(yīng)類java.lang.Class對象。當你鎖住一個類的時候,,你實際上是鎖住了這個類的Class對象,。

線程訪問對象實例或者類變量的時候不需要獲取鎖。但是如果一個線程獲取了一個鎖,,其他線程不能訪問被鎖住的數(shù)據(jù),,直到擁有鎖的線程釋放它。

管程

JVM 使用鎖和管程協(xié)作,。管程監(jiān)視一段代碼,,保證一個時間點內(nèi)只有一個線程能執(zhí)行這段代碼。

每個管程與一個對象引用關(guān)聯(lián),。當線程到達管程監(jiān)視代碼段的第一條指令時,,線程必須獲取關(guān)聯(lián)對象的鎖。線程不能執(zhí)行這段代碼直到它得到了鎖,。一旦它得到了鎖,,線程可以進入被保護的代碼段。

當線程離開被保護的代碼塊,,不管是如何離開的,,它都會釋放關(guān)聯(lián)對象的鎖。

多次鎖定

一個線程被允許鎖定一個對象多次,。對于每個對象,,JVM 維護了一個鎖的計數(shù)器。沒有被鎖的對象計數(shù)為 0,。當一個線程第一次獲取鎖,,計數(shù)器自增變?yōu)?1。每次這個線程(已經(jīng)得到鎖的線程)請求同一個對象的鎖,,計數(shù)器都會自增,。每次線程釋放鎖,計數(shù)器都會自減,。當計數(shù)器變?yōu)?0 時,,鎖才被釋放,可以給別的線程使用,。

同步塊

在 Java 語言的術(shù)語中,,協(xié)調(diào)多個線程訪問共享數(shù)據(jù)被稱為同步(synchronization)。Java 提供了兩種內(nèi)建的方式來同步對數(shù)據(jù)的訪問:

  1. 同步語句
  2. 同步方法

同步語句

為了創(chuàng)建同步語句,,你需要使用synchronized關(guān)鍵字,,括號里面是同步的對象引用,如下所示:

class KitchenSync { private int[] intArray = new int[10]; void reverseOrder() { synchronized (this) { int halfWay = intArray.length / 2; for (int i = 0; i < halfWay; ++i) { int upperIndex = intArray.length - 1 - i; int save = intArray[upperIndex]; intArray[upperIndex] = intArray[i]; intArray[i] = save; } } }}

在上面的例子中,被同步塊包含的語句不會被執(zhí)行,,直到線程得到this引用的對象鎖,。如果不是鎖住this引用,而是鎖住其他對象,,在線程執(zhí)行同步塊語句之前,,它需要獲得該對象的鎖。

有兩個字節(jié)碼monitorentermonitorexit,,被用來同步方法中的同步塊,。

字節(jié)碼操作數(shù)描述
monitorenter取出對象引用,請求與對象引用關(guān)聯(lián)的鎖
monitorexit取出對象引用,,釋放與對象引用關(guān)聯(lián)的鎖

monitorenter被 JVM 執(zhí)行時,,它請求棧頂對象引用關(guān)聯(lián)的鎖。如果該線程已經(jīng)擁有該對象的鎖,,計數(shù)器自增,。每次monitorexit被執(zhí)行,計數(shù)器自減,。當計數(shù)器變?yōu)?0 時,,該鎖被釋放。

注意:當同步塊中拋出異常時,,catch語句保證對象鎖被釋放,。不管同步塊是如何退出的,JVM 保證線程會釋放鎖,。

同步方法

為了同步整個方法,,你只需要在方法聲明前面加上synchronized關(guān)鍵字。

class HeatSync { private int[] intArray = new int[10]; synchronized void reverseOrder() { int halfWay = intArray.length / 2; for (int i = 0; i < halfWay; ++i) { int upperIndex = intArray.length - 1 - i; int save = intArray[upperIndex]; intArray[upperIndex] = intArray[i]; intArray[i] = save; } }}

JVM 不會使用特殊的字節(jié)碼來調(diào)用同步方法,。當 JVM 解析方法的符號引用時,,它會判斷方法是不是同步的。如果是,,JVM 要求線程在調(diào)用之前請求鎖,。對于實例方法,JVM 要求得到該實例對象的鎖,。對于類方法,,JVM 要求得到類鎖。在同步方法完成之后,,不管它是正常返回還是拋出異常,,鎖都會被釋放。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多