<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      安卓筆記俠

      專注安卓開發(fā)

      導航

      Java并發(fā)編程(三)Thread類的使用

      一.線程的狀態(tài)

      線程從創(chuàng)建到最終的消亡,要經歷若干個狀態(tài)。一般來說,線程包括以下這幾個狀態(tài):創(chuàng)建(new)、就緒(runnable)、運行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)。
      
      當需要新起一個線程來執(zhí)行某個子任務時,就創(chuàng)建了一個線程。但是線程創(chuàng)建之后,不會立即進入就緒狀態(tài),因為線程的運行需要一些條件(比如內存資源,在前面的JVM內存區(qū)域劃分一篇博文中知道程序計數器、Java棧、本地方法棧都是線程私有的,所以需要為線程分配一定的內存空間),只有線程運行需要的所有條件滿足了,才進入就緒狀態(tài)。
      
      當線程進入就緒狀態(tài)后,不代表立刻就能獲取CPU執(zhí)行時間,也許此時CPU正在執(zhí)行其他的事情,因此它要等待。當得到CPU執(zhí)行時間之后,線程便真正進入運行狀態(tài)。
      
      線程在運行狀態(tài)過程中,可能有多個原因導致當前線程不繼續(xù)運行下去,比如用戶主動讓線程睡眠(睡眠一定的時間之后再重新執(zhí)行)、用戶主動讓線程等待,或者被同步塊給阻塞,此時就對應著多個狀態(tài):time waiting(睡眠或等待一定的事件)、waiting(等待被喚醒)、blocked(阻塞)。
      
      當由于突然中斷或者子任務執(zhí)行完畢,線程就會被消亡。
      View Code

       

      下面這副圖描述了線程從創(chuàng)建到消亡之間的狀態(tài):

      在有些教程上將blocked、waiting、time waiting統(tǒng)稱為阻塞狀態(tài),這個也是可以的,只不過這里我想將線程的狀態(tài)和Java中的方法調用聯(lián)系起來,所以將waiting和time waiting兩個狀態(tài)分離出來。

       

      二.上下文切換

      對于單核CPU來說(對于多核CPU,此處就理解為一個核),CPU在一個時刻只能運行一個線程,當在運行一個線程的過程中轉去運行另外一個線程,這個叫做線程上下文切換(對于進程也是類似)。
      
      由于可能當前線程的任務并沒有執(zhí)行完畢,所以在切換時需要保存線程的運行狀態(tài),以便下次重新切換回來時能夠繼續(xù)切換之前的狀態(tài)運行。舉個簡單的例子:比如一個線程A正在讀取一個文件的內容,正讀到文件的一半,此時需要暫停線程A,轉去執(zhí)行線程B,當再次切換回來執(zhí)行線程A的時候,我們不希望線程A又從文件的開頭來讀取。
      
      因此需要記錄線程A的運行狀態(tài),那么會記錄哪些數據呢?因為下次恢復時需要知道在這之前當前線程已經執(zhí)行到哪條指令了,所以需要記錄程序計數器的值,另外比如說線程正在進行某個計算的時候被掛起了,那么下次繼續(xù)執(zhí)行的時候需要知道之前掛起時變量的值時多少,因此需要記錄CPU寄存器的狀態(tài)。所以一般來說,線程上下文切換過程中會記錄程序計數器、CPU寄存器狀態(tài)等數據。
      
      說簡單點的:對于線程的上下文切換實際上就是 存儲和恢復CPU狀態(tài)的過程,它使得線程執(zhí)行能夠從中斷點恢復執(zhí)行。
      
      雖然多線程可以使得任務執(zhí)行的效率得到提升,但是由于在線程切換時同樣會帶來一定的開銷代價,并且多個線程會導致系統(tǒng)資源占用的增加,所以在進行多線程編程時要注意這些因素。
      View Code

       

      三.Thread類中的方法

      通過查看java.lang.Thread類的源碼可知:

      Thread類實現了Runnable接口,在Thread類中,有一些比較關鍵的屬性,比如name是表示Thread的名字,可以通過Thread類的構造器中的參數來指定線程名字,priority表示線程的優(yōu)先級(最大值為10,最小值為1,默認值為5),daemon表示線程是否是守護線程,target表示要執(zhí)行的任務。

      下面是Thread類中常用的方法:
      以下是關系到線程運行狀態(tài)的幾個方法:

      1)start方法

      start()用來啟動一個線程,當調用start方法后,系統(tǒng)才會開啟一個新的線程來執(zhí)行用戶定義的子任務,在這個過程中,會為相應的線程分配需要的資源。
      View Code

      2)run方法

      run()方法是不需要用戶來調用的,當通過start方法啟動一個線程之后,當線程獲得了CPU執(zhí)行時間,便進入run方法體去執(zhí)行具體的任務。注意,繼承Thread類必須重寫run方法,在run方法中定義具體要執(zhí)行的任務。
      View Code

      3)sleep方法

      sleep方法有兩個重載版本:
      sleep(long millis)     //參數為毫秒
      sleep(long millis,int nanoseconds)    //第一參數為毫秒,第二個參數為納秒
      
      sleep相當于讓線程睡眠,交出CPU,讓CPU去執(zhí)行其他的任務。
      但是有一點要非常注意,sleep方法不會釋放鎖,也就是說如果當前線程持有對某個對象的鎖,則即使調用sleep方法,其他線程也無法訪問這個對象。看下面這個例子就清楚了:
      
      public class Test {
           
          private int i = 10;
          private Object object = new Object();
           
          public static void main(String[] args) throws IOException  {
              Test test = new Test();
              MyThread thread1 = test.new MyThread();
              MyThread thread2 = test.new MyThread();
              thread1.start();
              thread2.start();
          } 
           
           
          class MyThread extends Thread{
              @Override
              public void run() {
                  synchronized (object) {
                      i++;
                      System.out.println("i:"+i);
                      try {
                          System.out.println("線程"+Thread.currentThread().getName()+"進入睡眠狀態(tài)");
                          Thread.currentThread().sleep(10000);
                      } catch (InterruptedException e) {
                          // TODO: handle exception
                      }
                      System.out.println("線程"+Thread.currentThread().getName()+"睡眠結束");
                      i++;
                      System.out.println("i:"+i);
                  }
              }
          }
      }
      View Code

      輸出結果:

      從上面輸出結果可以看出,當Thread-0進入睡眠狀態(tài)之后,Thread-1并沒有去執(zhí)行具體的任務。只有當Thread-0執(zhí)行完之后,此時Thread-0釋放了對象鎖,Thread-1才開始執(zhí)行。

      注意,如果調用了sleep方法,必須捕獲InterruptedException異常或者將該異常向上層拋出。當線程睡眠時間滿后,不一定會立即得到執(zhí)行,因為此時可能CPU正在執(zhí)行其他的任務。所以說調用sleep方法相當于讓線程進入阻塞狀態(tài)。

      4)yield方法

      調用yield方法會讓當前線程交出CPU權限,讓CPU去執(zhí)行其他的線程。它跟sleep方法類似,同樣不會釋放鎖。但是yield不能控制具體的交出CPU的時間,另外,yield方法只能讓擁有相同優(yōu)先級的線程有獲取CPU執(zhí)行時間的機會。
      
      注意,調用yield方法并不會讓線程進入阻塞狀態(tài),而是讓線程重回就緒狀態(tài),它只需要等待重新獲取CPU執(zhí)行時間,這一點是和sleep方法不一樣的。
      View Code

      5)join方法

      join方法有三個重載版本:
      join()
      join(long millis)     //參數為毫秒
      join(long millis,int nanoseconds)    //第一參數為毫秒,第二個參數為納秒
      
      假如在main線程中,調用thread.join方法,則main方法會等待thread線程執(zhí)行完畢或者等待一定的時間。如果調用的是無參join方法,則等待thread執(zhí)行完畢,如果調用的是指定了時間參數的join方法,則等待一定的事件。
      
      看下面一個例子:
      
      public class Test {
           
          public static void main(String[] args) throws IOException  {
              System.out.println("進入線程"+Thread.currentThread().getName());
              Test test = new Test();
              MyThread thread1 = test.new MyThread();
              thread1.start();
              try {
                  System.out.println("線程"+Thread.currentThread().getName()+"等待");
                  thread1.join();
                  System.out.println("線程"+Thread.currentThread().getName()+"繼續(xù)執(zhí)行");
              } catch (InterruptedException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              }
          } 
           
          class MyThread extends Thread{
              @Override
              public void run() {
                  System.out.println("進入線程"+Thread.currentThread().getName());
                  try {
                      Thread.currentThread().sleep(5000);
                  } catch (InterruptedException e) {
                      // TODO: handle exception
                  }
                  System.out.println("線程"+Thread.currentThread().getName()+"執(zhí)行完畢");
              }
          }
      }
      View Code

      輸出結果:

      可以看出,當調用thread1.join()方法后,main線程會進入等待,然后等待thread1執(zhí)行完之后再繼續(xù)執(zhí)行。

      實際上調用join方法是調用了Object的wait方法,這個可以通過查看源碼得知:

       1 public final void join(long millis) throws InterruptedException {
       2     synchronized(lock) {
       3     long base = System.currentTimeMillis();
       4     long now = 0;
       5 
       6     if (millis < 0) {
       7         throw new IllegalArgumentException("timeout value is negative");
       8     }
       9 
      10     if (millis == 0) {
      11         while (isAlive()) {
      12             lock.wait(0);
      13         }
      14     } else {
      15         while (isAlive()) {
      16             long delay = millis - now;
      17             if (delay <= 0) {
      18                 break;
      19             }
      20             lock.wait(delay);
      21             now = System.currentTimeMillis() - base;
      22         }
      23     }
      24     }
      25 }
      View Code

      wait方法會讓線程進入阻塞狀態(tài),并且會釋放線程占有的鎖,并交出CPU執(zhí)行權限。

      由于wait方法會讓線程釋放對象鎖,所以join方法同樣會讓線程釋放對一個對象持有的鎖。具體的wait方法使用在后面文章中給出。

      6)interrupt方法

      interrupt,顧名思義,即中斷的意思。單獨調用interrupt方法可以使得處于阻塞狀態(tài)的線程拋出一個異常,也就說,它可以用來中斷一個正處于阻塞狀態(tài)的線程;另外,通過interrupt方法和isInterrupted()方法來停止正在運行的線程。
      
      下面看一個例子:
      public class Test {
           
          public static void main(String[] args) throws IOException  {
              Test test = new Test();
              MyThread thread = test.new MyThread();
              thread.start();
              try {
                  Thread.currentThread().sleep(2000);
              } catch (InterruptedException e) {
                   
              }
              thread.interrupt();
          } 
           
          class MyThread extends Thread{
              @Override
              public void run() {
                  try {
                      System.out.println("進入睡眠狀態(tài)");
                      Thread.currentThread().sleep(10000);
                      System.out.println("睡眠完畢");
                  } catch (InterruptedException e) {
                      System.out.println("得到中斷異常");
                  }
                  System.out.println("run方法執(zhí)行完畢");
              }
          }
      }
      View Code

      輸出結果:

       1 從這里可以看出,通過interrupt方法可以中斷處于阻塞狀態(tài)的線程。那么能不能中斷處于非阻塞狀態(tài)的線程呢?看下面這個例子:
       2 
       3 public class Test {
       4      
       5     public static void main(String[] args) throws IOException  {
       6         Test test = new Test();
       7         MyThread thread = test.new MyThread();
       8         thread.start();
       9         try {
      10             Thread.currentThread().sleep(2000);
      11         } catch (InterruptedException e) {
      12              
      13         }
      14         thread.interrupt();
      15     } 
      16      
      17     class MyThread extends Thread{
      18         @Override
      19         public void run() {
      20             int i = 0;
      21             while(i<Integer.MAX_VALUE){
      22                 System.out.println(i+" while循環(huán)");
      23                 i++;
      24             }
      25         }
      26     }
      27 }
      28 
      29 運行該程序會發(fā)現,while循環(huán)會一直運行直到變量i的值超出Integer.MAX_VALUE。所以說直接調用interrupt方法不能中斷正在運行中的線程。
      30 
      31 但是如果配合isInterrupted()能夠中斷正在運行的線程,因為調用interrupt方法相當于將中斷標志位置為true,那么可以通過調用isInterrupted()判斷中斷標志是否被置位來中斷線程的執(zhí)行。比如下面這段代碼:
      32 
      33 public class Test {
      34      
      35     public static void main(String[] args) throws IOException  {
      36         Test test = new Test();
      37         MyThread thread = test.new MyThread();
      38         thread.start();
      39         try {
      40             Thread.currentThread().sleep(2000);
      41         } catch (InterruptedException e) {
      42              
      43         }
      44         thread.interrupt();
      45     } 
      46      
      47     class MyThread extends Thread{
      48         @Override
      49         public void run() {
      50             int i = 0;
      51             while(!isInterrupted() && i<Integer.MAX_VALUE){
      52                 System.out.println(i+" while循環(huán)");
      53                 i++;
      54             }
      55         }
      56     }
      57 }
      58 
      59 運行會發(fā)現,打印若干個值之后,while循環(huán)就停止打印了。
      60 
      61 但是一般情況下不建議通過這種方式來中斷線程,一般會在MyThread類中增加一個屬性 isStop來標志是否結束while循環(huán),然后再在while循環(huán)中判斷isStop的值。
      62 
      63 class MyThread extends Thread{
      64         private volatile boolean isStop = false;
      65         @Override
      66         public void run() {
      67             int i = 0;
      68             while(!isStop){
      69                 i++;
      70             }
      71         }
      72          
      73         public void setStop(boolean stop){
      74             this.isStop = stop;
      75         }
      76 }
      77 
      78 那么就可以在外面通過調用setStop方法來終止while循環(huán)。
      View Code

      7)stop方法

      stop方法已經是一個廢棄的方法,它是一個不安全的方法。因為調用stop方法會直接終止run方法的調用,并且會拋出一個ThreadDeath錯誤,如果線程持有某個對象鎖的話,會完全釋放鎖,導致對象狀態(tài)不一致。所以stop方法基本是不會被用到的。
      View Code

      8)destroy方法

      destroy方法也是廢棄的方法。基本不會被使用到。
      
      以下是關系到線程屬性的幾個方法:
      1)getId
      用來得到線程ID
      
      2)getName和setName
      用來得到或者設置線程名稱。
      
      3)getPriority和setPriority
      用來獲取和設置線程優(yōu)先級。
      
      4)setDaemon和isDaemon
      用來設置線程是否成為守護線程和判斷線程是否是守護線程。
      守護線程和用戶線程的區(qū)別在于:守護線程依賴于創(chuàng)建它的線程,而用戶線程則不依賴。舉個簡單的例子:如果在main線程中創(chuàng)建了一個守護線程,當main方法運行完畢之后,守護線程也會隨著消亡。而用戶線程則不會,用戶線程會一直運行直到其運行完畢。在JVM中,像垃圾收集器線程就是守護線程。
      
      Thread類有一個比較常用的靜態(tài)方法currentThread()用來獲取當前線程。
      
      在上面已經說到了Thread類中的大部分方法,那么Thread類中的方法調用到底會引起線程狀態(tài)發(fā)生怎樣的變化呢?下面一幅圖就是在上面的圖上進行改進而來的:
      View Code

       

        

       

      posted on 2016-12-04 17:44  安卓筆記俠  閱讀(597)  評論(0)    收藏  舉報

      主站蜘蛛池模板: 久久精品国产亚洲αv忘忧草 | 综合无码一区二区三区| 深夜视频国产在线观看| 99久久免费精品国产色| 国产精品午夜av福利| 精品人妻午夜一区二区三区四区| 成人国产一区二区三区精品| 国产亚洲一区二区三区av| 17岁日本免费bd完整版观看| 欧美一本大道香蕉综合视频| 国产成人综合在线观看不卡| 亚洲欧美色一区二区三区| 精品国产一区av天美传媒| 国产人妻人伦精品婷婷| 华人在线亚洲欧美精品| 丰满多毛的大隂户视频| 亚洲色婷婷一区二区三区| 免费AV片在线观看网址| 成熟丰满熟妇av无码区| 国产成人夜色高潮福利app| 久久这里都是精品二| 无码精品人妻一区二区三区中| 久久99精品久久久久久| 国产成人高清亚洲一区91| 国产精品SM捆绑调教视频| 国语精品自产拍在线观看网站| 国产精品入口麻豆| 国内精品伊人久久久久AV一坑 | 永久免费AV无码网站YY| 爱性久久久久久久久| 国产精品一区二区麻豆蜜桃 | 国产亚洲一区二区三区av| 午夜DY888国产精品影院| 乐安县| 精品无码国产污污污免费| 又大又粗欧美成人网站| 亚洲国产天堂久久综合226114| 国产精品福利午夜久久香蕉| 亚洲大尺度无码无码专线| 国产高清精品在线91| 资源新版在线天堂偷自拍|