多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > php教程 > Java并發編程的藝術(六)——線程間的通信

Java并發編程的藝術(六)——線程間的通信

來源:程序員人生   發布時間:2018-06-11 17:28:16 閱讀次數:10103次

多條線程之間有時需要數據交互,下面介紹5種線程間數據交互的方式,他們的使用處景各有不同。

1. volatile、synchronized關鍵字

PS:關于volatile的詳細介紹請移步至:Java并發編程的藝術(3)——volatile

1.1 如何實現通訊?

這兩種方式都采取了同步機制實現多條線程間的數據通訊。與其說是“通訊”,倒不如說是“同享變量”來的恰當。當1個同享變量被volatile修飾 或 被同步塊包裹后,他們的讀寫操作都會直接操作同享內存,從而各個線程都能看到同享變量最新的值,也就是實現了內存的可見性。

1.2 特點

  • 這類方式本質上是“同享數據”,而非“傳遞數據”;只是從結果來看,數據好像是從寫線程傳遞到了讀線程;
  • 這類通訊方式沒法指定特定的接收線程。當數據被修改后究竟哪條線程最早訪問到,這由操作系統隨機決定。
  • 總的來講,這類方式其實不是真正意義上的“通訊”,而是“同享”。

1.3 使用處景

這類方式能“傳遞”變量。當需要傳遞1些公用的變量時就能夠使用這類方式。如:傳遞boolean flag,用于表示狀態、傳遞1個存儲所有任務的隊列等。

1.4 例子

用這類方式實現線程的開關控制。

// 用于控制線程當前的履行狀態 private volatile boolean running = false; // 開啟1條線程 Thread thread = new Thread(new Runnable(){ void run(){ // 開關 while(!running){
            Thread.sleep(1000);
        } // 履行線程任務 doSometing();
    }
}).start(); // 開始履行 public void start(){
    running = true;
}

2. 等待/通知機制

2.1 如何實現?

等待/通知機制的實現由Java完成,我們只需調用Object類的幾個方法便可。

  • wait():將當前線程的狀態改成“等待態”,加入等待隊列,釋放鎖;直到當前線程產生中斷或調用了notify方法,這條線程才會被從等待隊列轉移到同步隊列,此時可以開始競爭鎖。
  • wait(long):和wait()功能1樣,只不過量了個超時動作。1旦超時,就會繼續履行wait以后的代碼,它不會拋超時異常!
  • notify():將等待隊列中的1條線程轉移到同步隊列中去。
  • notifyAll():將等待隊列中的所有線程都轉移到同步隊列中去。

2.2 注意點

  • 以上方法都必須放在同步塊中;
  • 并且以上方法都只能由所處同步塊的鎖對象調用;
  • 鎖對象A.notify()/notifyAll()只能喚醒由鎖對象A wait的線程;
  • 調用notify/notifyAll函數后僅僅是將線程從等待隊列轉移到阻塞隊列,只有當該線程競爭到鎖后,才能從wait方法中返回,繼續履行接下來的代碼;

2.3 QA

  • 為何wait必須放在同步塊中調用?
    由于等待/通知機制需要和同享狀態變量配合使用,1般是先檢查狀態,若狀態為true則履行wait,即包括“先檢查后履行”,因此需要把這1進程加鎖,確保其原子履行。
    舉個例子:
// 同享的狀態變量 boolean flag = false; // 線程1 Thread t1 = new Thread(new Runnable(){ public void run(){ while(!flag){
            wait();
        }
    }
}).start(); // 線程2 Thread t2 = new Thread(new Runnable(){ public void run(){
        flag = true;
        notifyAll();
    }
}).start();

上述例子thread1未加同步。當thread1履行到while那行后,判斷其狀態為true,此時若產生上下文切換,線程2開始履行,并1口氣履行完了;此時flag已是true,但是thread1繼續履行,遇到wait后便進入等待態;但此時已沒有線程能喚醒它了,因此就1直等待下去。

  • 為何notify需要加鎖?且必須和wait使用同1把鎖?
    首先,加鎖是為了保證同享變量的內存可見性,讓它產生修改后能直接寫入同享內存,好讓wait所處的線程立即看見。
    其次,和wait使用同1把鎖是為了確保wait、notify之間的互斥,即:同1時刻,只能有其中1條線程履行。

  • 為何必須使用同步塊的鎖對象調用wait函數?
    首先,由于wait會釋放鎖,因此通過鎖對象調用wait就是告知wait釋放哪一個鎖。
    其次,告知線程,你是在哪一個鎖對象上等待的,只有當該鎖對象調用notify時你才能被喚醒。

  • 為何必須使用同步塊的鎖對象調用notify函數?
    告知notify,只喚醒在該鎖對象上等待的線程。

2.4 代碼實現

等待/通知機制用于實現生產者和消費者模式。

  • 生產者
synchronized(鎖A){
    flag = true;// 或:list.add(xx); 鎖A.notify();
}
  • 消費者
synchronized(鎖A){ // 不滿足條件 while(!flag){ // 或:list.isEmpty() 鎖A.wait();
    } // doSometing…… }

2.5 超時等待模式

在之前的生產者-消費者模式中,如果生產者沒有發出通知,那末消費者將永久等待下去。為了不這類情況,我們可以給消費者增加超時等待功能。該功能依托于wait(long)方法,只需在wait前的檢查條件中增加超時標識位,實現以下:

public void get(long mills){
    synchronized( list ){ // 不加超時功能 if ( mills <= 0 ) { while( list.isEmpty() ){ list.wait();
            }
        } // 添加超時功能 else {
            boolean isTimeout = false; while(list.isEmpty() && isTimeout){ list.wait(mills);
                isTimeout = true;
            } // doSometing…… }
    }
}

3. 管道流

3.1 作用

管道流用于在兩個線程之間進行字節流或字符流的傳遞。

3.2 特點

  • 管道流的實現依托PipedOutputStream、PipedInputStream、PipedWriter、PipedReader。分別對應字節流和字符流。
  • 他們與IO流的區分是:IO流是在硬盤、內存、Socket之間活動,而管道流僅在內存中的兩條線程間活動。

3.3 實現

步驟以下:
1. 在1條線程中分別創建輸入流和輸出流;
2. 將輸入流和輸出留連接起來;
3. 將輸入流和輸出流分外傳遞給兩條線程;
4. 調用read和write方法就能夠實現線程間通訊。

// 創建輸入流與輸出流對象 PipedWriter out = new PipedWriter();
PipedReader in = new PipedReader(); // 連接輸入輸出流 out.connect(in); // 創建寫線程 class WriteThread extends Thread{ private PipedWriter out; public WriteThread(PipedWriter out){ this.out = out;
    } public void run(){
        out.write("hello concurrent world!");
    }
} // 創建讀線程 class ReaderThread extends Thread{ private PipedReader in; public ReaderThread(PipedReader in){ this.in = in;
    } public void run(){
        in.read();
    }
} // 

4. join

4.1 作用

  • join能將并發履行的多條線程串行履行;
  • join函數屬于Thread類,通過1個thread對象調用。當在線程B中履行threadA.join()時,線程B將會被阻塞(底層調用wait方法),等到threadA線程運行結束后才會返回join方法。
  • 被等待的那條線程可能會履行很長時間,因此join函數會拋出InterruptedException。當調用threadA.interrupt()后,join函數就會拋出該異常。

4.2 實現

public static void main(String[] args){ // 開啟1條線程 Thread t = new Thread(new Runnable(){ public void run(){ // doSometing }
    }).start(); // 調用join,等待t線程履行終了 try{
        t.join();
    }catch(InterruptedException e){ // 中斷處理…… }

}


生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 久久精品免费一区二区三区 | 日韩欧美第一页 | 最近最新免费中文字幕高清 | 伊人网在线视频观看 | 午夜免费播放观看在线视频 | 亚洲五月七月丁香缴情 | 日韩欧美亚洲精品 | 久久精品成人一区二区三区 | 一本久道久久综合中文字幕 | 亚洲最大色网站 | 亚洲欧美日韩国产综合久 | 欧美性受xxxx| 精品成人一区二区 | 日本高清免费网站zzzzzzzz | 国产精品永久免费视频 | 亚洲线精品一区二区三区 | 国内精自视频品线六区免费 | 伊人久久国产 | 国产精品欧美一区二区在线看 | 亚洲成人免费 | 国产精品高清全国免费观看 | 黄大色黄美女精品大毛片 | 日本中文字幕一区 | 五月天 婷 | 日本高清在线看 | 波多野结衣免费视频观看 | 久久久不卡国产精品一区二区 | 免费xxxxx在线观看网站 | 欧美最猛性xxxx免费 | 禁视频网站在线观看漫画 | 午夜在线| 欧美日韩激情一区二区三区 | 中文字幕第一 | 最近免费字幕高清在线观看 | 性xxxxx大片免费视频 | 欧美另类xxxxx69高清 | 免费jizz在线播放 | 精品国产午夜肉伦伦影院 | 色一情一伦一区二区三 | 性高湖久久久久久久久aaaaa | 国产在线一区二区视频 |