一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - Java利用happen-before規(guī)則如何實現(xiàn)共享變量的同步操作詳解

Java利用happen-before規(guī)則如何實現(xiàn)共享變量的同步操作詳解

2021-05-07 11:10莫那-魯?shù)?/span> Java教程

這篇文章主要給大家介紹了關(guān)于Java利用happen-before規(guī)則實現(xiàn)共享變量的同步操作的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

熟悉 java 并發(fā)編程的都知道,jmm(java 內(nèi)存模型) 中的 happen-before(簡稱 hb)規(guī)則,該規(guī)則定義了 java 多線程操作的有序性和可見性,防止了編譯器重排序?qū)Τ绦蚪Y(jié)果的影響。

java語言中有一個“先行發(fā)生”(happen—before)的規(guī)則,它是java內(nèi)存模型中定義的兩項操作之間的偏序關(guān)系,如果操作a先行發(fā)生于操作b,其意思就是說,在發(fā)生操作b之前,操作a產(chǎn)生的影響都能被操作b觀察到,“影響”包括修改了內(nèi)存中共享變量的值、發(fā)送了消息、調(diào)用了方法等,它與時間上的先后發(fā)生基本沒有太大關(guān)系。這個原則特別重要,它是判斷數(shù)據(jù)是否存在競爭、線程是否安全的主要依據(jù)。

按照官方的說法:

當(dāng)一個變量被多個線程讀取并且至少被一個線程寫入時,如果讀操作和寫操作沒有 hb 關(guān)系,則會產(chǎn)生數(shù)據(jù)競爭問題。

要想保證操作 b 的線程看到操作 a 的結(jié)果(無論 a 和 b 是否在一個線程),那么在 a 和 b 之間必須滿足 hb 原則,如果沒有,將有可能導(dǎo)致重排序。

當(dāng)缺少 hb 關(guān)系時,就可能出現(xiàn)重排序問題。

hb 有哪些規(guī)則?

這個大家都非常熟悉了應(yīng)該,大部分書籍和文章都會介紹,這里稍微回顧一下:

  • 程序次序規(guī)則:一個線程內(nèi),按照代碼順序,書寫在前面的操作先行發(fā)生于書寫在后面的操作;
  • 鎖定規(guī)則:在監(jiān)視器鎖上的解鎖操作必須在同一個監(jiān)視器上的加鎖操作之前執(zhí)行。
  • volatile變量規(guī)則:對一個變量的寫操作先行發(fā)生于后面對這個變量的讀操作;
  • 傳遞規(guī)則:如果操作a先行發(fā)生于操作b,而操作b又先行發(fā)生于操作c,則可以得出操作a先行發(fā)生于操作c;
  • 線程啟動規(guī)則:thread對象的start()方法先行發(fā)生于此線程的每一個動作;
  • 線程中斷規(guī)則:對線程interrupt()方法的調(diào)用先行發(fā)生于被中斷線程的代碼檢測到中斷事件的發(fā)生;
  • 線程終結(jié)規(guī)則:線程中所有的操作都先行發(fā)生于線程的終止檢測,我們可以通過thread.join()方法結(jié)束、thread.isalive()的返回值手段檢測到線程已經(jīng)終止執(zhí)行;
  • 對象終結(jié)規(guī)則:一個對象的初始化完成先行發(fā)生于他的finalize()方法的開始;

其中,傳遞規(guī)則我加粗了,這個規(guī)則至關(guān)重要。如何熟練的使用傳遞規(guī)則是實現(xiàn)同步的關(guān)鍵。

然后,再換個角度解釋 hb:當(dāng)一個操作 a hb 操作 b,那么,操作 a 對共享變量的操作結(jié)果對操作 b 都是可見的。

同時,如果 操作 b hb 操作 c,那么,操作 a 對共享變量的操作結(jié)果對操作 b 都是可見的。

而實現(xiàn)可見性的原理則是 cache protocol 和 memory barrier。通過緩存一致性協(xié)議和內(nèi)存屏障實現(xiàn)可見性。

如何實現(xiàn)同步?

在 doug lea 著作 《java concurrency in practice》中,有下面的描述:

Java利用happen-before規(guī)則如何實現(xiàn)共享變量的同步操作詳解

書中提到:通過組合 hb 的一些規(guī)則,可以實現(xiàn)對某個未被鎖保護變量的可見性。

但由于這個技術(shù)對語句的順序很敏感,因此容易出錯。

樓主接下來,將演示如何通過 volatile 規(guī)則和程序次序規(guī)則實現(xiàn)對一個變量同步。

來一個熟悉的例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class threadprintdemo {
 
 static int num = 0;
 static volatile boolean flag = false;
 
 public static void main(string[] args) {
 
 thread t1 = new thread(() -> {
  for (; 100 > num; ) {
  if (!flag && (num == 0 || ++num % 2 == 0)) {
   system.out.println(num);
   flag = true;
  }
  }
 }
 );
 
 thread t2 = new thread(() -> {
  for (; 100 > num; ) {
  if (flag && (++num % 2 != 0)) {
   system.out.println(num);
   flag = false;
  }
  }
 }
 );
 
 t1.start();
 t2.start();
 }
}

這段代碼的作用是兩個線程間隔打印出 0 - 100 的數(shù)字。

熟悉并發(fā)編程的同學(xué)肯定要說了,這個 num 變量沒有使用 volatile,會有可見性問題,即:t1 線程更新了 num,t2 線程無法感知。

哈哈,樓主剛開始也是這么認為的,但最近通過研究 hb 規(guī)則,我發(fā)現(xiàn),去掉 num 的 volatile 修飾也是可以的。

我們分析一下,樓主畫了一個圖:

Java利用happen-before規(guī)則如何實現(xiàn)共享變量的同步操作詳解

我們分析這個圖:

  • 首先,紅色和黃色表示不同的線程操作。
  • 紅色線程對 num 變量做 ++,然后修改了 volatile 變量,這個是符合 程序次序規(guī)則的。也就是 1 hb 2.
  • 紅色線程對 volatile 的寫 hb 黃色線程對 volatile 的讀,也就是 2 hb 3.
  • 黃色線程讀取 volatile 變量,然后對 num 變量做 ++,符合 程序次序規(guī)則,也就是 3 hb 4.
  • 根據(jù)傳遞性規(guī)則,1 肯定 hb 4. 所以,1 的修改對 4來說都是可見的。

注意:hb 規(guī)則保證上一個操作的結(jié)果對下一個操作都是可見的。

所以,上面的小程序中,線程 a 對 num 的修改,線程 b 是完全感知的 —— 即使 num 沒有使用 volatile 修飾。

這樣,我們就借助 hb 原則實現(xiàn)了對一個變量的同步操作,也就是在多線程環(huán)境中,保證了并發(fā)修改共享變量的安全性。并且沒有對這個變量使用 java 的原語:volatile 和 synchronized 和 cas(假設(shè)算的話)。

這可能看起來不安全(實際上安全),也好像不太容易理解。因為這一切都是 hb 底層的 cache protocol 和 memory barrier 實現(xiàn)的。

其他規(guī)則實現(xiàn)同步

利用線程終結(jié)規(guī)則實現(xiàn):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static int a = 1;
 
public static void main(string[] args) {
thread tb = new thread(() -> {
 a = 2;
});
thread ta = new thread(() -> {
 try {
 tb.join();
 } catch (interruptedexception e) {
 //no
 }
 system.out.println(a);
});
 
ta.start();
tb.start();
}

利用線程 start 規(guī)則實現(xiàn):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
static int a = 1;
 
public static void main(string[] args) {
thread tb = new thread(() -> {
 system.out.println(a);
});
thread ta = new thread(() -> {
 tb.start();
 a = 2;
});
 
ta.start();
}

這兩個操作,也可以保證變量 a 的可見性。

確實有點顛覆之前的觀念。之前的觀念中,如果一個變量沒有被 volatile 修飾或 final 修飾,那么他在多線程下的讀寫肯定是不安全的 —— 因為會有緩存,導(dǎo)致讀取到的不是最新的。

然而,通過借助 hb,我們可以實現(xiàn)。

總結(jié)

雖然本文標題是通過 happen-before 實現(xiàn)對共享變量的同步操作,但主要目的還是更深刻的理解 happen-before,理解他的 happen-before 概念其實就是保證多線程環(huán)境中,上一個操作對下一個操作的有序性和操作結(jié)果的可見性。

同時,通過靈活的使用傳遞性規(guī)則,再對規(guī)則進行組合,就可以將兩個線程進行同步 —— 實現(xiàn)指定的共享變量不使用原語也可以保證可見性。雖然這好像不是很易讀,但也是一種嘗試。

關(guān)于如何組合使用規(guī)則實現(xiàn)同步,doug lea 在 juc 中給出了實踐。

例如老版本的 futuretask 的內(nèi)部類 sync(已消失),通過 tryreleaseshared 方法修改 volatile 變量,tryacquireshared 讀取 volatile 變量,這是利用了 volatile 規(guī)則;

通過在 tryreleaseshared 之前設(shè)置非 volatile 的 result 變量,然后在 tryacquireshared 之后讀取 result 變量,這是利用了程序次序規(guī)則。

從而保證 result 變量的可見性。和我們的第一個例子類似:利用程序次序規(guī)則和 volatile 規(guī)則實現(xiàn)普通變量可見性。

而 doug lea 自己也說了,這個“借助”技術(shù)非常容易出錯,要謹慎使用。但在某些情況下,這種“借助”是非常合理的。

實際上,blockingqueue 也是“借助”了 happen-before 的規(guī)則。還記得 unlock 規(guī)則嗎?當(dāng) unlock 發(fā)生后,內(nèi)部元素一定是可見的。

而類庫中還有其他的操作也“借助”了 happen-before 原則:并發(fā)容器,countdownlatch,semaphore,future,executor,cyclicbarrier,exchanger 等。

總而言之,言而總之:

happen-before 原則是 jmm 的核心所在,只有滿足了 hb 原則才能保證有序性和可見性,否則編譯器將會對代碼重排序。hb 甚至將 lock 和 volatile 也定義了規(guī)則。

通過適當(dāng)?shù)膶?hb 規(guī)則的組合,可以實現(xiàn)對普通共享變量的正確使用。

好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對服務(wù)器之家的支持。

原文鏈接:https://www.cnblogs.com/stateis0/p/9124060.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 香蕉tv亚洲专区在线观看 | 18未年禁止免费观看 | 鬼吹灯之天星术免费观看 | 欧美午夜寂寞影院安卓列表 | 国产1区2区在线观看 | 四虎欧美 | 欧美一区二区三区精品国产 | 国产精品成人一区二区 | 青青草成人在线 | 天天爱天天操天天射 | 性满足久久久久久久久 | 久久偷拍国2017的 | 欧美肥胖老妇做爰变态 | 亚洲国产精品一区二区三区久久 | 97精品国产高清在线看入口 | 日本黄色大片免费观看 | 精品一区二区三区高清免费不卡 | 嗯啊视频在线观看 | 女被男啪到哭 | 午夜国产在线视频 | 91精品国产综合久久 | 精品午夜寂寞影院在线观看 | 桃色综合网| 色综合色狠狠天天综合色hd | 好湿好滑好硬好爽好深视频 | yellow视频在线观看免费 | 亚洲国产精品自产在线播放 | 99久久99热久久精品免费看 | 火影小南被爆羞羞网站进入 | 日本人妖视频 | 亚洲国产日韩成人综合天堂 | 欧美日韩一区二区中文字幕视频 | 狠狠躁夜夜躁人人爽天天miya | 婷婷麻豆 | 欧美日韩国产在线一区 | 大学生初次破苞免费视频 | 狠狠干快播 | 大ji巴好好爽好深网站 | 草久社区 | 午夜毛片在线观看 | 色婷婷综合久久久中文字幕 |