欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java線程編程中Thread類(lèi)的基礎(chǔ)學(xué)習(xí)教程

 更新時(shí)間:2015年12月07日 17:25:58   作者:海子  
這篇文章主要介紹了Java線程編程中Thread類(lèi)的基礎(chǔ)學(xué)習(xí)教程,Thread類(lèi)包含諸多操作線程的方法,非常重要,需要的朋友可以參考下

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

  在正式學(xué)習(xí)Thread類(lèi)中的具體方法之前,我們先來(lái)了解一下線程有哪些狀態(tài),這個(gè)將會(huì)有助于后面對(duì)Thread類(lèi)中的方法的理解。

  線程從創(chuàng)建到最終的消亡,要經(jīng)歷若干個(gè)狀態(tài)。一般來(lái)說(shuō),線程包括以下這幾個(gè)狀態(tài):創(chuàng)建(new)、就緒(runnable)、運(yùn)行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)。

  當(dāng)需要新起一個(gè)線程來(lái)執(zhí)行某個(gè)子任務(wù)時(shí),就創(chuàng)建了一個(gè)線程。但是線程創(chuàng)建之后,不會(huì)立即進(jìn)入就緒狀態(tài),因?yàn)榫€程的運(yùn)行需要一些條件(比如內(nèi)存資源,在前面的JVM內(nèi)存區(qū)域劃分一篇博文中知道程序計(jì)數(shù)器、Java棧、本地方法棧都是線程私有的,所以需要為線程分配一定的內(nèi)存空間),只有線程運(yùn)行需要的所有條件滿足了,才進(jìn)入就緒狀態(tài)。

  當(dāng)線程進(jìn)入就緒狀態(tài)后,不代表立刻就能獲取CPU執(zhí)行時(shí)間,也許此時(shí)CPU正在執(zhí)行其他的事情,因此它要等待。當(dāng)?shù)玫紺PU執(zhí)行時(shí)間之后,線程便真正進(jìn)入運(yùn)行狀態(tài)。

  線程在運(yùn)行狀態(tài)過(guò)程中,可能有多個(gè)原因?qū)е庐?dāng)前線程不繼續(xù)運(yùn)行下去,比如用戶主動(dòng)讓線程睡眠(睡眠一定的時(shí)間之后再重新執(zhí)行)、用戶主動(dòng)讓線程等待,或者被同步塊給阻塞,此時(shí)就對(duì)應(yīng)著多個(gè)狀態(tài):time waiting(睡眠或等待一定的事件)、waiting(等待被喚醒)、blocked(阻塞)。

  當(dāng)由于突然中斷或者子任務(wù)執(zhí)行完畢,線程就會(huì)被消亡。

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

2015127171725022.jpg (778×492)

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

二.上下文切換

  對(duì)于單核CPU來(lái)說(shuō)(對(duì)于多核CPU,此處就理解為一個(gè)核),CPU在一個(gè)時(shí)刻只能運(yùn)行一個(gè)線程,當(dāng)在運(yùn)行一個(gè)線程的過(guò)程中轉(zhuǎn)去運(yùn)行另外一個(gè)線程,這個(gè)叫做線程上下文切換(對(duì)于進(jìn)程也是類(lèi)似)。

  由于可能當(dāng)前線程的任務(wù)并沒(méi)有執(zhí)行完畢,所以在切換時(shí)需要保存線程的運(yùn)行狀態(tài),以便下次重新切換回來(lái)時(shí)能夠繼續(xù)切換之前的狀態(tài)運(yùn)行。舉個(gè)簡(jiǎn)單的例子:比如一個(gè)線程A正在讀取一個(gè)文件的內(nèi)容,正讀到文件的一半,此時(shí)需要暫停線程A,轉(zhuǎn)去執(zhí)行線程B,當(dāng)再次切換回來(lái)執(zhí)行線程A的時(shí)候,我們不希望線程A又從文件的開(kāi)頭來(lái)讀取。

  因此需要記錄線程A的運(yùn)行狀態(tài),那么會(huì)記錄哪些數(shù)據(jù)呢?因?yàn)橄麓位謴?fù)時(shí)需要知道在這之前當(dāng)前線程已經(jīng)執(zhí)行到哪條指令了,所以需要記錄程序計(jì)數(shù)器的值,另外比如說(shuō)線程正在進(jìn)行某個(gè)計(jì)算的時(shí)候被掛起了,那么下次繼續(xù)執(zhí)行的時(shí)候需要知道之前掛起時(shí)變量的值時(shí)多少,因此需要記錄CPU寄存器的狀態(tài)。所以一般來(lái)說(shuō),線程上下文切換過(guò)程中會(huì)記錄程序計(jì)數(shù)器、CPU寄存器狀態(tài)等數(shù)據(jù)。

  說(shuō)簡(jiǎn)單點(diǎn)的:對(duì)于線程的上下文切換實(shí)際上就是 存儲(chǔ)和恢復(fù)CPU狀態(tài)的過(guò)程,它使得線程執(zhí)行能夠從中斷點(diǎn)恢復(fù)執(zhí)行。

  雖然多線程可以使得任務(wù)執(zhí)行的效率得到提升,但是由于在線程切換時(shí)同樣會(huì)帶來(lái)一定的開(kāi)銷(xiāo)代價(jià),并且多個(gè)線程會(huì)導(dǎo)致系統(tǒng)資源占用的增加,所以在進(jìn)行多線程編程時(shí)要注意這些因素。

三.Thread類(lèi)中的方法

  通過(guò)查看java.lang.Thread類(lèi)的源碼可知:

2015127171921431.jpg (666×732)

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

  下面是Thread類(lèi)中常用的方法:

  以下是關(guān)系到線程運(yùn)行狀態(tài)的幾個(gè)方法:

  1)start方法

  start()用來(lái)啟動(dòng)一個(gè)線程,當(dāng)調(diào)用start方法后,系統(tǒng)才會(huì)開(kāi)啟一個(gè)新的線程來(lái)執(zhí)行用戶定義的子任務(wù),在這個(gè)過(guò)程中,會(huì)為相應(yīng)的線程分配需要的資源。

  2)run方法

  run()方法是不需要用戶來(lái)調(diào)用的,當(dāng)通過(guò)start方法啟動(dòng)一個(gè)線程之后,當(dāng)線程獲得了CPU執(zhí)行時(shí)間,便進(jìn)入run方法體去執(zhí)行具體的任務(wù)。注意,繼承Thread類(lèi)必須重寫(xiě)run方法,在run方法中定義具體要執(zhí)行的任務(wù)。

  3)sleep方法

  sleep方法有兩個(gè)重載版本:


sleep(long millis)   //參數(shù)為毫秒
 
sleep(long millis,int nanoseconds)  //第一參數(shù)為毫秒,第二個(gè)參數(shù)為納秒

  sleep相當(dāng)于讓線程睡眠,交出CPU,讓CPU去執(zhí)行其他的任務(wù)。

  但是有一點(diǎn)要非常注意,sleep方法不會(huì)釋放鎖,也就是說(shuō)如果當(dāng)前線程持有對(duì)某個(gè)對(duì)象的鎖,則即使調(diào)用sleep方法,其他線程也無(wú)法訪問(wèn)這個(gè)對(duì)象??聪旅孢@個(gè)例子就清楚了:


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()+"進(jìn)入睡眠狀態(tài)");
          Thread.currentThread().sleep(10000);
        } catch (InterruptedException e) {
          // TODO: handle exception
        }
        System.out.println("線程"+Thread.currentThread().getName()+"睡眠結(jié)束");
        i++;
        System.out.println("i:"+i);
      }
    }
  }
}

   輸出結(jié)果:

2015127171957572.jpg (625×214)

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

  注意,如果調(diào)用了sleep方法,必須捕獲InterruptedException異常或者將該異常向上層拋出。當(dāng)線程睡眠時(shí)間滿后,不一定會(huì)立即得到執(zhí)行,因?yàn)榇藭r(shí)可能CPU正在執(zhí)行其他的任務(wù)。所以說(shuō)調(diào)用sleep方法相當(dāng)于讓線程進(jìn)入阻塞狀態(tài)。

  4)yield方法

  調(diào)用yield方法會(huì)讓當(dāng)前線程交出CPU權(quán)限,讓CPU去執(zhí)行其他的線程。它跟sleep方法類(lèi)似,同樣不會(huì)釋放鎖。但是yield不能控制具體的交出CPU的時(shí)間,另外,yield方法只能讓擁有相同優(yōu)先級(jí)的線程有獲取CPU執(zhí)行時(shí)間的機(jī)會(huì)。

  注意,調(diào)用yield方法并不會(huì)讓線程進(jìn)入阻塞狀態(tài),而是讓線程重回就緒狀態(tài),它只需要等待重新獲取CPU執(zhí)行時(shí)間,這一點(diǎn)是和sleep方法不一樣的。

  5)join方法

  join方法有三個(gè)重載版本:

join()
join(long millis)   //參數(shù)為毫秒
join(long millis,int nanoseconds)  //第一參數(shù)為毫秒,第二個(gè)參數(shù)為納秒

   假如在main線程中,調(diào)用thread.join方法,則main方法會(huì)等待thread線程執(zhí)行完畢或者等待一定的時(shí)間。如果調(diào)用的是無(wú)參join方法,則等待thread執(zhí)行完畢,如果調(diào)用的是指定了時(shí)間參數(shù)的join方法,則等待一定的事件。

  看下面一個(gè)例子:

public class Test {
   
  public static void main(String[] args) throws IOException {
    System.out.println("進(jìn)入線程"+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("進(jìn)入線程"+Thread.currentThread().getName());
      try {
        Thread.currentThread().sleep(5000);
      } catch (InterruptedException e) {
        // TODO: handle exception
      }
      System.out.println("線程"+Thread.currentThread().getName()+"執(zhí)行完畢");
    }
  }
}

   輸出結(jié)果:

 2015127172052230.jpg (656×155)

  可以看出,當(dāng)調(diào)用thread1.join()方法后,main線程會(huì)進(jìn)入等待,然后等待thread1執(zhí)行完之后再繼續(xù)執(zhí)行。

  實(shí)際上調(diào)用join方法是調(diào)用了Object的wait方法,這個(gè)可以通過(guò)查看源碼得知:

2015127172222602.jpg (649×517)  

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

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

  6)interrupt方法

  interrupt,顧名思義,即中斷的意思。單獨(dú)調(diào)用interrupt方法可以使得處于阻塞狀態(tài)的線程拋出一個(gè)異常,也就說(shuō),它可以用來(lái)中斷一個(gè)正處于阻塞狀態(tài)的線程;另外,通過(guò)interrupt方法和isInterrupted()方法來(lái)停止正在運(yùn)行的線程。

  下面看一個(gè)例子:

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("進(jìn)入睡眠狀態(tài)");
        Thread.currentThread().sleep(10000);
        System.out.println("睡眠完畢");
      } catch (InterruptedException e) {
        System.out.println("得到中斷異常");
      }
      System.out.println("run方法執(zhí)行完畢");
    }
  }
}

輸出結(jié)果:

2015127172256485.jpg (624×115)

從這里可以看出,通過(guò)interrupt方法可以中斷處于阻塞狀態(tài)的線程。那么能不能中斷處于非阻塞狀態(tài)的線程呢?看下面這個(gè)例子:

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() {
      int i = 0;
      while(i<Integer.MAX_VALUE){
        System.out.println(i+" while循環(huán)");
        i++;
      }
    }
  }
}

   運(yùn)行該程序會(huì)發(fā)現(xiàn),while循環(huán)會(huì)一直運(yùn)行直到變量i的值超出Integer.MAX_VALUE。所以說(shuō)直接調(diào)用interrupt方法不能中斷正在運(yùn)行中的線程。

  但是如果配合isInterrupted()能夠中斷正在運(yùn)行的線程,因?yàn)檎{(diào)用interrupt方法相當(dāng)于將中斷標(biāo)志位置為true,那么可以通過(guò)調(diào)用isInterrupted()判斷中斷標(biāo)志是否被置位來(lái)中斷線程的執(zhí)行。比如下面這段代碼:

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() {
      int i = 0;
      while(!isInterrupted() && i<Integer.MAX_VALUE){
        System.out.println(i+" while循環(huán)");
        i++;
      }
    }
  }
}

   運(yùn)行會(huì)發(fā)現(xiàn),打印若干個(gè)值之后,while循環(huán)就停止打印了。

  但是一般情況下不建議通過(guò)這種方式來(lái)中斷線程,一般會(huì)在MyThread類(lèi)中增加一個(gè)屬性 isStop來(lái)標(biāo)志是否結(jié)束while循環(huán),然后再在while循環(huán)中判斷isStop的值。

class MyThread extends Thread{
    private volatile boolean isStop = false;
    @Override
    public void run() {
      int i = 0;
      while(!isStop){
        i++;
      }
    }
     
    public void setStop(boolean stop){
      this.isStop = stop;
    }
  }

   那么就可以在外面通過(guò)調(diào)用setStop方法來(lái)終止while循環(huán)。

  7)stop方法

  stop方法已經(jīng)是一個(gè)廢棄的方法,它是一個(gè)不安全的方法。因?yàn)檎{(diào)用stop方法會(huì)直接終止run方法的調(diào)用,并且會(huì)拋出一個(gè)ThreadDeath錯(cuò)誤,如果線程持有某個(gè)對(duì)象鎖的話,會(huì)完全釋放鎖,導(dǎo)致對(duì)象狀態(tài)不一致。所以stop方法基本是不會(huì)被用到的。

  8)destroy方法

  destroy方法也是廢棄的方法。基本不會(huì)被使用到。

  以下是關(guān)系到線程屬性的幾個(gè)方法:

  1)getId

  用來(lái)得到線程ID

  2)getName和setName

  用來(lái)得到或者設(shè)置線程名稱。

  3)getPriority和setPriority

  用來(lái)獲取和設(shè)置線程優(yōu)先級(jí)。

  4)setDaemon和isDaemon

  用來(lái)設(shè)置線程是否成為守護(hù)線程和判斷線程是否是守護(hù)線程。

  守護(hù)線程和用戶線程的區(qū)別在于:守護(hù)線程依賴于創(chuàng)建它的線程,而用戶線程則不依賴。舉個(gè)簡(jiǎn)單的例子:如果在main線程中創(chuàng)建了一個(gè)守護(hù)線程,當(dāng)main方法運(yùn)行完畢之后,守護(hù)線程也會(huì)隨著消亡。而用戶線程則不會(huì),用戶線程會(huì)一直運(yùn)行直到其運(yùn)行完畢。在JVM中,像垃圾收集器線程就是守護(hù)線程。

  Thread類(lèi)有一個(gè)比較常用的靜態(tài)方法currentThread()用來(lái)獲取當(dāng)前線程。

  在上面已經(jīng)說(shuō)到了Thread類(lèi)中的大部分方法,那么Thread類(lèi)中的方法調(diào)用到底會(huì)引起線程狀態(tài)發(fā)生怎樣的變化呢?下面一幅圖就是在上面的圖上進(jìn)行改進(jìn)而來(lái)的:

2015127172336080.jpg (778×531)

相關(guān)文章

  • java使用RestTemplate封裝post請(qǐng)求方式

    java使用RestTemplate封裝post請(qǐng)求方式

    這篇文章主要介紹了java使用RestTemplate封裝post請(qǐng)求方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java項(xiàng)目日志脫敏解決方案

    Java項(xiàng)目日志脫敏解決方案

    為了保護(hù)客戶的敏感信息,我們提出了四種日志脫敏處理方案,需要根據(jù)實(shí)際項(xiàng)目需求和情況進(jìn)行選擇,新項(xiàng)目或重構(gòu)的項(xiàng)目,對(duì)于項(xiàng)目標(biāo)準(zhǔn)化要求較高的,推薦使用方案四,感興趣的朋友參考下本文
    2024-10-10
  • springcloud中Feign超時(shí)提示Read timed out executing POST的問(wèn)題及解決方法

    springcloud中Feign超時(shí)提示Read timed out executing

    Feign接口調(diào)用分兩層,Ribbon的調(diào)用和Hystrix調(diào)用,理論上設(shè)置Ribbon的時(shí)間即可,但是Ribbon的超時(shí)時(shí)間和Hystrix的超時(shí)時(shí)間需要結(jié)合起來(lái),這篇文章給大家介紹springcloud之Feign超時(shí)提示Read timed out executing POST問(wèn)題及解決方法,感興趣的朋友一起看看吧
    2024-01-01
  • mybatis plus開(kāi)發(fā)過(guò)程中遇到的問(wèn)題記錄及解決

    mybatis plus開(kāi)發(fā)過(guò)程中遇到的問(wèn)題記錄及解決

    這篇文章主要介紹了mybatis plus開(kāi)發(fā)過(guò)程中遇到的問(wèn)題記錄及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Java使用Hutool+自定義注解實(shí)現(xiàn)數(shù)據(jù)脫敏

    Java使用Hutool+自定義注解實(shí)現(xiàn)數(shù)據(jù)脫敏

    我們?cè)谑褂檬謾C(jī)銀行的時(shí)候經(jīng)常能看到APP上會(huì)將銀行卡的卡號(hào)中間部分給隱藏掉使用 ***** 來(lái)代替,在某些網(wǎng)站上查看一些業(yè)務(wù)密碼時(shí)(例如簽到密碼等)也會(huì)使用 ***** 來(lái)隱藏掉真正的密碼,那么這種方式是如何實(shí)現(xiàn)的呢,本文將給大家介紹使用Hutool+自定義注解實(shí)現(xiàn)數(shù)據(jù)脫敏
    2023-09-09
  • Java實(shí)現(xiàn)音頻轉(zhuǎn)碼(WAV、MP3、AMR互轉(zhuǎn))

    Java實(shí)現(xiàn)音頻轉(zhuǎn)碼(WAV、MP3、AMR互轉(zhuǎn))

    本文主要介紹了Java實(shí)現(xiàn)音頻轉(zhuǎn)碼,包括WAV、MP3、AMR互轉(zhuǎn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-02-02
  • IntelliJ IDEA彈出“IntelliJ IDEA License Activation”的處理方法

    IntelliJ IDEA彈出“IntelliJ IDEA License Activation”的處理方法

    這篇文章主要介紹了IntelliJ IDEA彈出“IntelliJ IDEA License Activation”的處理方法,本文給出解決方法,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Java并發(fā)內(nèi)存模型詳情

    Java并發(fā)內(nèi)存模型詳情

    這篇文章主要介紹了Java并發(fā)內(nèi)存模型,Java是一門(mén)支持多線程執(zhí)行的語(yǔ)言,要編寫(xiě)正確的并發(fā)程序,了解Java內(nèi)存模型是重要前提。而了解硬件內(nèi)存模型有助于理解程序的執(zhí)行,下面文章就來(lái)看看詳細(xì)內(nèi)容吧
    2021-10-10
  • Java集合TreeSet用法詳解

    Java集合TreeSet用法詳解

    本文詳細(xì)講解了Java集合TreeSet用法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-12-12
  • springboot靜態(tài)資源的配置方式

    springboot靜態(tài)資源的配置方式

    這篇文章主要介紹了springboot靜態(tài)資源的配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05

最新評(píng)論