JAVA多線程之線程間的通信方式

一售躁,介紹

本總結(jié)我對于JAVA多線程中線程之間的通信方式的理解,主要以代碼結(jié)合文字的方式來討論線程間的通信,故摘抄了書中的一些示例代碼鞋怀。


二,線程間的通信方式

①同步

這里講的同步是指多個(gè)線程通過synchronized關(guān)鍵字這種方式來實(shí)現(xiàn)線程間的通信持搜。

參考示例:

public class MyObject {

? ? synchronized public void methodA() {

? ? ? ? //do something....

? ? }

? ? synchronized public void methodB() {

? ? ? ? //do some other thing

? ? }

}

public class ThreadA extends Thread {

? ? private MyObject object;

//省略構(gòu)造方法

? ? @Override

? ? public void run() {

? ? ? ? super.run();

? ? ? ? object.methodA();

? ? }

}

public class ThreadB extends Thread {

? ? private MyObject object;

//省略構(gòu)造方法

? ? @Override

? ? public void run() {

? ? ? ? super.run();

? ? ? ? object.methodB();

? ? }

}

public class Run {

? ? public static void main(String[] args) {

? ? ? ? MyObject object = new MyObject();

? ? ? ? //線程A與線程B 持有的是同一個(gè)對象:object

? ? ? ? ThreadA a = new ThreadA(object);

? ? ? ? ThreadB b = new ThreadB(object);

? ? ? ? a.start();

? ? ? ? b.start();

? ? }

}

由于線程A和線程B持有同一個(gè)MyObject類的對象object密似,盡管這兩個(gè)線程需要調(diào)用不同的方法,但是它們是同步執(zhí)行的葫盼,比如:線程B需要等待線程A執(zhí)行完了methodA()方法之后残腌,它才能執(zhí)行methodB()方法。這樣,線程A和線程B就實(shí)現(xiàn)了 通信抛猫。

這種方式蟆盹,本質(zhì)上就是“共享內(nèi)存”式的通信。多個(gè)線程需要訪問同一個(gè)共享變量闺金,誰拿到了鎖(獲得了訪問權(quán)限)逾滥,誰就可以執(zhí)行。


②while輪詢的方式

代碼如下:

1 import java.util.ArrayList;

2 import java.util.List;

3

4 public class MyList {

5

6? ? private List<String> list = new ArrayList<String>();

7? ? public void add() {

8? ? ? ? list.add("elements");

9? ? }

10? ? public int size() {

11? ? ? ? return list.size();

12? ? }

13 }

14

15 import mylist.MyList;

16

17 public class ThreadA extends Thread {

18

19? ? private MyList list;

20

21? ? public ThreadA(MyList list) {

22? ? ? ? super();

23? ? ? ? this.list = list;

24? ? }

25

26? ? @Override

27? ? public void run() {

28? ? ? ? try {

29? ? ? ? ? ? for (int i = 0; i < 10; i++) {

30? ? ? ? ? ? ? ? list.add();

31? ? ? ? ? ? ? ? System.out.println("添加了" + (i + 1) + "個(gè)元素");

32? ? ? ? ? ? ? ? Thread.sleep(1000);

33? ? ? ? ? ? }

34? ? ? ? } catch (InterruptedException e) {

35? ? ? ? ? ? e.printStackTrace();

36? ? ? ? }

37? ? }

38 }

39

40 import mylist.MyList;

41

42 public class ThreadB extends Thread {

43

44? ? private MyList list;

45

46? ? public ThreadB(MyList list) {

47? ? ? ? super();

48? ? ? ? this.list = list;

49? ? }

50

51? ? @Override

52? ? public void run() {

53? ? ? ? try {

54? ? ? ? ? ? while (true) {

55? ? ? ? ? ? ? ? if (list.size() == 5) {

56? ? ? ? ? ? ? ? ? ? System.out.println("==5, 線程b準(zhǔn)備退出了");

57? ? ? ? ? ? ? ? ? ? throw new InterruptedException();

58? ? ? ? ? ? ? ? }

59? ? ? ? ? ? }

60? ? ? ? } catch (InterruptedException e) {

61? ? ? ? ? ? e.printStackTrace();

62? ? ? ? }

63? ? }

64 }

65

66 import mylist.MyList;

67 import extthread.ThreadA;

68 import extthread.ThreadB;

69

70 public class Test {

71

72? ? public static void main(String[] args) {

73? ? ? ? MyList service = new MyList();

74

75? ? ? ? ThreadA a = new ThreadA(service);

76? ? ? ? a.setName("A");

77? ? ? ? a.start();

78

79? ? ? ? ThreadB b = new ThreadB(service);

80? ? ? ? b.setName("B");

81? ? ? ? b.start();

82? ? }

83 }

在這種方式下败匹,線程A不斷地改變條件寨昙,線程ThreadB不停地通過while語句檢測這個(gè)條件(list.size()==5)是否成立 ,從而實(shí)現(xiàn)了線程間的通信掀亩。但是這種方式會(huì)浪費(fèi)CPU資源舔哪。之所以說它浪費(fèi)資源,是因?yàn)镴VM調(diào)度器將CPU交給線程B執(zhí)行時(shí)槽棍,它沒做啥“有用”的工作捉蚤,只是在不斷地測試 某個(gè)條件是否成立。就類似于現(xiàn)實(shí)生活中炼七,某個(gè)人一直看著手機(jī)屏幕是否有電話來了缆巧,而不是: 在干別的事情,當(dāng)有電話來時(shí)特石,響鈴?fù)ㄖ猅A電話來了盅蝗。關(guān)于線程的輪詢的影響,可參考:JAVA多線程之當(dāng)一個(gè)線程在執(zhí)行死循環(huán)時(shí)會(huì)影響另外一個(gè)線程嗎姆蘸?

這種方式還存在另外一個(gè)問題:

輪詢的條件的可見性問題墩莫,關(guān)于內(nèi)存可見性問題,可參考:JAVA多線程之volatile 與 synchronized 的比較中的第一點(diǎn)“一逞敷,volatile關(guān)鍵字的可見性

線程都是先把變量讀取到本地線程椏袂兀空間,然后再去再去修改的本地變量推捐。因此裂问,如果線程B每次都在取本地的 條件變量,那么盡管另外一個(gè)線程已經(jīng)改變了輪詢的條件牛柒,它也察覺不到堪簿,這樣也會(huì)造成死循環(huán)。


③wait/notify機(jī)制

代碼如下:

1 import java.util.ArrayList;

2 import java.util.List;

3

4 public class MyList {

5

6? ? private static List<String> list = new ArrayList<String>();

7

8? ? public static void add() {

9? ? ? ? list.add("anyString");

10? ? }

11

12? ? public static int size() {

13? ? ? ? return list.size();

14? ? }

15 }

16

17

18 public class ThreadA extends Thread {

19

20? ? private Object lock;

21

22? ? public ThreadA(Object lock) {

23? ? ? ? super();

24? ? ? ? this.lock = lock;

25? ? }

26

27? ? @Override

28? ? public void run() {

29? ? ? ? try {

30? ? ? ? ? ? synchronized (lock) {

31? ? ? ? ? ? ? ? if (MyList.size() != 5) {

32? ? ? ? ? ? ? ? ? ? System.out.println("wait begin "

33? ? ? ? ? ? ? ? ? ? ? ? ? ? + System.currentTimeMillis());

34? ? ? ? ? ? ? ? ? ? lock.wait();

35? ? ? ? ? ? ? ? ? ? System.out.println("wait end? "

36? ? ? ? ? ? ? ? ? ? ? ? ? ? + System.currentTimeMillis());

37? ? ? ? ? ? ? ? }

38? ? ? ? ? ? }

39? ? ? ? } catch (InterruptedException e) {

40? ? ? ? ? ? e.printStackTrace();

41? ? ? ? }

42? ? }

43 }

44

45

46 public class ThreadB extends Thread {

47? ? private Object lock;

48

49? ? public ThreadB(Object lock) {

50? ? ? ? super();

51? ? ? ? this.lock = lock;

52? ? }

53

54? ? @Override

55? ? public void run() {

56? ? ? ? try {

57? ? ? ? ? ? synchronized (lock) {

58? ? ? ? ? ? ? ? for (int i = 0; i < 10; i++) {

59? ? ? ? ? ? ? ? ? ? MyList.add();

60? ? ? ? ? ? ? ? ? ? if (MyList.size() == 5) {

61? ? ? ? ? ? ? ? ? ? ? ? lock.notify();

62? ? ? ? ? ? ? ? ? ? ? ? System.out.println("已經(jīng)發(fā)出了通知");

63? ? ? ? ? ? ? ? ? ? }

64? ? ? ? ? ? ? ? ? ? System.out.println("添加了" + (i + 1) + "個(gè)元素!");

65? ? ? ? ? ? ? ? ? ? Thread.sleep(1000);

66? ? ? ? ? ? ? ? }

67? ? ? ? ? ? }

68? ? ? ? } catch (InterruptedException e) {

69? ? ? ? ? ? e.printStackTrace();

70? ? ? ? }

71? ? }

72 }

73

74 public class Run {

75

76? ? public static void main(String[] args) {

77

78? ? ? ? try {

79? ? ? ? ? ? Object lock = new Object();

80

81? ? ? ? ? ? ThreadA a = new ThreadA(lock);

82? ? ? ? ? ? a.start();

83

84? ? ? ? ? ? Thread.sleep(50);

85

86? ? ? ? ? ? ThreadB b = new ThreadB(lock);

87? ? ? ? ? ? b.start();

88? ? ? ? } catch (InterruptedException e) {

89? ? ? ? ? ? e.printStackTrace();

90? ? ? ? }

91? ? }

92 }

線程A要等待某個(gè)條件滿足時(shí)(list.size()==5)皮壁,才執(zhí)行操作椭更。線程B則向list中添加元素,改變list 的size蛾魄。

A,B之間如何通信的呢虑瀑?也就是說湿滓,線程A如何知道 list.size() 已經(jīng)為5了呢?

這里用到了Object類的 wait() 和 notify() 方法舌狗。

當(dāng)條件未滿足時(shí)(list.size() !=5)叽奥,線程A調(diào)用wait() 放棄CPU,并進(jìn)入阻塞狀態(tài)痛侍。---不像②while輪詢那樣占用CPU

當(dāng)條件滿足時(shí)朝氓,線程B調(diào)用 notify()通知 線程A,所謂通知線程A主届,就是喚醒線程A膀篮,并讓它進(jìn)入可運(yùn)行狀態(tài)。

這種方式的一個(gè)好處就是CPU的利用率提高了岂膳。

但是也有一些缺點(diǎn):比如,線程B先執(zhí)行磅网,一下子添加了5個(gè)元素并調(diào)用了notify()發(fā)送了通知谈截,而此時(shí)線程A還執(zhí)行;當(dāng)線程A執(zhí)行并調(diào)用wait()時(shí)涧偷,那它永遠(yuǎn)就不可能被喚醒了簸喂。因?yàn)椋€程B已經(jīng)發(fā)了通知了燎潮,以后不再發(fā)通知了喻鳄。這說明:通知過早,會(huì)打亂程序的執(zhí)行邏輯确封。


④管道通信就是使用java.io.PipedInputStream 和 java.io.PipedOutputStream進(jìn)行通信

具體就不介紹了除呵。分布式系統(tǒng)中說的兩種通信機(jī)制:共享內(nèi)存機(jī)制和消息通信機(jī)制。感覺前面的①中的synchronized關(guān)鍵字和②中的while輪詢 “屬于” 共享內(nèi)存機(jī)制爪喘,由于是輪詢的條件使用了volatile關(guān)鍵字修飾時(shí)颜曾,這就表示它們通過判斷這個(gè)“共享的條件變量“是否改變了,來實(shí)現(xiàn)進(jìn)程間的交流秉剑。

而管道通信泛豪,更像消息傳遞機(jī)制,也就是說:通過管道侦鹏,將一個(gè)線程中的消息發(fā)送給另一個(gè)诡曙。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市略水,隨后出現(xiàn)的幾起案子价卤,更是在濱河造成了極大的恐慌,老刑警劉巖聚请,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荠雕,死亡現(xiàn)場離奇詭異稳其,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)炸卑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門既鞠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人盖文,你說我怎么就攤上這事嘱蛋。” “怎么了五续?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵洒敏,是天一觀的道長。 經(jīng)常有香客問我疙驾,道長凶伙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任它碎,我火速辦了婚禮函荣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扳肛。我一直安慰自己傻挂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布挖息。 她就那樣靜靜地躺著金拒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪套腹。 梳的紋絲不亂的頭發(fā)上绪抛,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機(jī)與錄音沉迹,去河邊找鬼睦疫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛鞭呕,可吹牛的內(nèi)容都是我干的蛤育。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼葫松,長吁一口氣:“原來是場噩夢啊……” “哼瓦糕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起腋么,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤咕娄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后珊擂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體圣勒,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡费变,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了圣贸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挚歧。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖吁峻,靈堂內(nèi)的尸體忽然破棺而出滑负,到底是詐尸還是另有隱情,我是刑警寧澤用含,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布矮慕,位于F島的核電站,受9級特大地震影響啄骇,放射性物質(zhì)發(fā)生泄漏痴鳄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一缸夹、第九天 我趴在偏房一處隱蔽的房頂上張望夏跷。 院中可真熱鬧,春花似錦明未、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至佣蓉,卻和暖如春披摄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背勇凭。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工疚膊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人虾标。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓寓盗,卻偏偏與公主長得像,于是被迫代替她去往敵國和親璧函。 傳聞我的和親對象是個(gè)殘疾皇子傀蚌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容