Java 線程間通信

線程間的交互和通信

  1. 一個(gè)線程啟動(dòng)另一個(gè)線程

    public static void main(String[] args){
      new Thread().start();
    }
    

    主線程啟動(dòng)另一個(gè)線程。

  2. 一個(gè)線程終結(jié)另個(gè)一線程
    A. Thread.stop()來停止一個(gè)線程,但是已經(jīng)被棄用了,因?yàn)?code>stop結(jié)果是不可預(yù)期的,它是直接將線程干掉了,即是你的業(yè)務(wù)剛執(zhí)行到一半,這樣就導(dǎo)致結(jié)果是不可預(yù)期的,比較危險(xiǎn)。

      Thread thread = new Thread();
      thread.start();
      thread.stop();
    

    B. Thread.interrupt()來中斷一個(gè)線程的執(zhí)行。它的原理是調(diào)用interrupt方法后,將該線程編輯為中斷狀態(tài),需要我們自己處理這個(gè)狀態(tài)。

      Thread thread = new Thread(){
          @Override
          public void run(){
              if(isInterrupted()){
                  //do something
              }
          }
      };
      thread.start();
      thread.interrupt();
    

    判斷一個(gè)線程是否標(biāo)記為中斷狀態(tài),可以使用thread.isInterrupted(),也可以使用Thead的靜態(tài)方法Thread.interruped(),二者的區(qū)別是前者可以重復(fù)多次使用,后者只能使用一次,因?yàn)槭褂弥髸?huì)將標(biāo)記復(fù)位,即置為false,實(shí)際使用根據(jù)自己具體的邏輯來具體使用哪種方式。

    C. 當(dāng)一個(gè)線程正在休眠中,此時(shí)被中斷,會(huì)怎樣?
    答案是會(huì)立即拋出InterruptedException,然后線程會(huì)被立即結(jié)束,但是中斷標(biāo)志一直是false。因?yàn)楫?dāng)處于休眠狀態(tài),說明這個(gè)線程目前沒有在處理事情,也就是說可以立即打斷,也不會(huì)產(chǎn)生影響,所以會(huì)被立即終止。這也是當(dāng)我們寫Thread.sleep(10); 的時(shí)候要求強(qiáng)制捕獲InterruptedException的原因,所以如果我們有需要?jiǎng)t要在異常中處理中斷的后續(xù)邏輯。

  3. 兩個(gè)線程互相配合
    需要使用wait()notify()以及notifyAll(),這里有必要說明下,這3個(gè)方法是針對monitor來說的,并不是針對線程,需要等的是monitor,需要喚醒的也是monitor,和線程無關(guān),而且這3個(gè)方法也必須是在同步代碼塊中才可以使用。
    下面我們來簡單說一下waitnotify的工作模型。

    工作模型

首先我們看下這段代碼,有兩個(gè)同步方法methodAmethodB,他們的鎖對象都是this,即monitor是同一個(gè),下面來說明下這個(gè)工作模型。

當(dāng)ThreadA先訪問methodB時(shí),它先來到等待訪問排隊(duì)區(qū),發(fā)現(xiàn)里面沒人,就他自己,于是它從monitor那里拿到了鎖,此時(shí)的線程正在訪問區(qū)里面放的是ThreadA。但是methodB里面的代碼是wait(),此時(shí)monitor會(huì)將ThreadA放到右面的等待喚醒區(qū),同時(shí)ThreadA所持有的鎖也會(huì)被釋放,正在訪問區(qū)是空的。這時(shí)ThreadB來了,想訪問methodA,它問了下monitor,我可以訪問methodA嗎,因?yàn)榇藭r(shí)的ThreadA的鎖已經(jīng)釋放了,它理所當(dāng)然的可以訪問methodA,此時(shí)的線程正在訪問區(qū)中里面放的是ThreadB, ThreadB拿著鎖來到了methodA內(nèi)部,此時(shí)它發(fā)現(xiàn)里面是notifyAll,所以此時(shí)的monitor就將等待喚醒區(qū)里面的所有等待喚醒的線程都喚醒了,然后ThreadB就是放了鎖,然后罵罵咧咧的走了,此時(shí)線程正在訪問區(qū)是空的。等待喚醒區(qū)中的ThreadA被喚醒,他只能再次來到等待訪問排隊(duì)區(qū),不能插隊(duì)到正在訪問區(qū)中,只能公平的再次去競爭鎖,然后發(fā)下排隊(duì)的就他自己,于是它又從monitor那里拿到了鎖,然后就去訪問methodB了,但是里面又是wait(),于是乎它又釋放鎖,然后又進(jìn)入了等待喚醒區(qū),可以沒有人再去訪問methodA來告訴monitor區(qū)喚醒喚醒等待區(qū)里面的線程了,此時(shí)ThreadA將永遠(yuǎn)在等待喚醒區(qū)等一輩子。

大致的一個(gè)工作模型就是這樣的,可能描述的不太清楚,但是大致原理是清晰的,相信多看幾遍總會(huì)明白的,哈哈哈。。。

這里為什么調(diào)用的是nitofyAll呢,而不是notify呢?因?yàn)獒槍@個(gè)monitor而言,有可能等待喚醒區(qū)有好多個(gè)線程正在等待,而不是一個(gè),如果只喚醒一個(gè),那剩下的咋辦?所以只能一次性喚醒所有的,除非你明確知道等待喚醒區(qū)中只有一個(gè)線程在等待被喚醒。
所以從這個(gè)工作模型來看,wait,notify 以及notifyAll 都是針對monitor而言的,是monitor讓一個(gè)線程區(qū)等待喚醒區(qū),也是monitor來進(jìn)行喚醒等待喚醒區(qū)里面的線程,而且waitnotify一定是成對出現(xiàn)的。
下面舉一個(gè)簡單的栗子。

 public class Demo {

 public static void main(String[] args) {
     new WaitNotifyTest().runTest();
 }

 private static class WaitNotifyTest{

     private int x;

     void runTest() {
         Thread threadA = new Thread(){
             @Override
             public void run() {
                 try {
                     Thread.sleep(2000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 plus();
             }
         };
         threadA.start();
         Thread threadB = new Thread(){
             @Override
             public void run() {
                 try {
                     Thread.sleep(1000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 printX();
             }
         };
         threadB.start();
     }

     private synchronized void plus() {
         x++;
         notifyAll();
     }

     private synchronized void printX() {
         if (x == 0) {
             try {
                 wait();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
         System.out.println("The x is: " + x);
     }

 }
}

執(zhí)行結(jié)果如下:


結(jié)果
  1. 插隊(duì)
    將一個(gè)線程插到當(dāng)前線程之前執(zhí)行,插隊(duì)的方法就是join
    簡單代碼如下:
    public static void main(String[] args) {
        Thread threadA = new Thread(){
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("run method in ThreadA class is finished。");
            }
        };
        threadA.start();
        try {
            threadA.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main method is finished。");
    }
    

    上述代碼執(zhí)行的結(jié)果:


    執(zhí)行結(jié)果

當(dāng)ThreadA執(zhí)行了join插隊(duì)方法后,main方法所在的線程會(huì)等ThradA線程執(zhí)行結(jié)果后在執(zhí)行。
join可以說是簡化后的wait,notify,只是不需要我們調(diào)用notify。

它的作用時(shí)將并行的兩個(gè)線程變成串行化。

  1. yield。暫時(shí)讓出時(shí)間片,讓給同優(yōu)先級的線程,這個(gè)時(shí)間很短很短,讓出之后馬上會(huì)再次獲取到時(shí)間片。

由于個(gè)人能力有限,如有錯(cuò)誤之處,還望指出,我會(huì)第一時(shí)間驗(yàn)證并修改。
理解事物,看本質(zhì)。共勉。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 摘自《Java并發(fā)編程的藝術(shù)》 1 volatile和synchronized關(guān)鍵字 關(guān)鍵字volatile可以用...
    沉淪2014閱讀 629評論 0 2
  • 現(xiàn)在計(jì)算機(jī)和智能手機(jī)都是多核處理器,為了更好地發(fā)揮設(shè)備的性能,提高應(yīng)用程序的體驗(yàn)性,多線程是必不可少的技術(shù)。線程之...
    落英墜露閱讀 2,511評論 0 4
  • 線程間的通信 線程間通信:多個(gè)線程在處理同一資源,但是任務(wù)卻不同 1 多線程執(zhí)行同一資源: 2 多線程執(zhí)行同一資源...
    柳子陌閱讀 661評論 0 1
  • 一,介紹 本總結(jié)我對于JAVA多線程中線程之間的通信方式的理解,主要以代碼結(jié)合文字的方式來討論線程間的通信,故摘抄...
    那些年的代碼閱讀 211評論 0 0
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭,有人歡樂有人憂愁,有人驚喜有人失落,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,879評論 28 54

友情鏈接更多精彩內(nèi)容