亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java多線程基本用法總結(jié)

 更新時間:2017年01月04日 09:59:51   作者:byhieg  
本篇文章主要總結(jié)了Java線程的一些基本的用法。具有一定的參考價值,下面跟著小編一起來看下吧

這篇是Java多線程基本用法的一個總結(jié)。

本篇文章會從一下幾個方面來說明Java多線程的基本用法:

  1. 如何使用多線程
  2. 如何得到多線程的一些信息
  3. 如何停止線程
  4. 如何暫停線程
  5. 線程的一些其他用法

如何使用多線程

啟動線程的兩種方式

Java 提供了2種方式來使用多線程, 一種是編寫一個類來繼承Thread,然后覆寫run方法,然后調(diào)用start方法來啟動線程。這時這個類就會以另一個線程的方式來運行run方法里面的代碼。另一種是編寫一個類來實現(xiàn)Runnable接口,然后實現(xiàn)接口方法run,然后創(chuàng)造一個Thread對象,把實現(xiàn)了Runnable接口的類當做構(gòu)造參數(shù),傳入Thread對象,最后該Thread對象調(diào)用start方法。

這里的start方法是一個有啟動功能的方法,該方法內(nèi)部回調(diào)run方法。所以,只有調(diào)用了start方法才會啟動另一個線程,直接調(diào)用run方法,還是在同一個線程中執(zhí)行run,而不是在另一個線程執(zhí)行run

此外,start方法只是告訴虛擬機,該線程可以啟動了,也就說該線程在就緒的狀態(tài),但不代表調(diào)用start就立即運行了,這要等待JVM來決定什么時候執(zhí)行這個線程。也就是說,如果有兩個線程A,B ,A先調(diào)用start,B后調(diào)用start,不代表A線程先運行,B線程后運行。這都是由JVM決定了,可以認為是隨機啟動。

下面我們用實際的代碼,來說明兩種啟動線程的方式:

第一種,繼承Thread

public class ExampleThread extends Thread{
  @Override
  public void run() {
    super.run();
    System.out.println("這是一個繼承自Thread的ExampleThread");
  }
}

測試的代碼可以看test目錄下的ExampleThreadTest

另一種,實現(xiàn)了Runnable接口

public class ExampleRunable implements Runnable{
  public void run() {
    System.out.println("這是實現(xiàn)Runnable接口的類");
  }
}

測試的代碼可以看test目錄下的ExampleRunableTest類。

如何得到多線程的一些信息

我們在啟動多線程之后,希望能通過一些API得到啟動的線程的一些信息。JDK給我們提供了一個Thread類的方法來得到線程的一些信息。

  • 線程的名字 —— getName()
  • 線程的ID —— getId()
  • 線程是否存活 —— isAlive()

得到線程的名字

這些方法是屬于Thread的內(nèi)部方法,所以我們可以用兩種方式調(diào)用這些方法,一個是我們的類繼承Thread來使用多線程的時候,可以用過this來調(diào)用。另一種是通過Thread.currentThread() 來調(diào)用這些方法。但是這兩個方法在不同的使用場景下是有區(qū)別的。

我們先簡單來看兩個方法的使用。

第一個Thread.currentThread()的使用,代碼如下:

public class ExampleCurrentThread extends Thread{
  public ExampleCurrentThread(){
    System.out.println("構(gòu)造方法的打?。? + Thread.currentThread().getName());
  }
  @Override
  public void run() {
    super.run();
    System.out.println("run方法的打?。? + Thread.currentThread().getName());
  }
}

測試的代碼如下:

public class ExampleCurrentThreadTest extends TestCase {
  public void testInit() throws Exception{
    ExampleCurrentThread thread = new ExampleCurrentThread();
  }
  public void testRun() throws Exception {
    ExampleCurrentThread thread = new ExampleCurrentThread();
    thread.start();
    Thread.sleep(1000);
  }
}

結(jié)果如下:

構(gòu)造方法的打?。簃ain
run方法的打?。篢hread-0
構(gòu)造方法的打?。簃ain

為什么我們在ExampleCurrentThread內(nèi)部用Thread.currentThread()會顯示構(gòu)造方法的打印是main,是因為Thread.currentThread()返回的是代碼段正在被那個線程調(diào)用的信息。這里面很顯然構(gòu)造方法是被main線程執(zhí)行的,而run方法是被我們自己啟動的線程執(zhí)行的,因為沒有給他起名字,所以默認是Thread-0。

接下來,我們在看一看繼承自Thread,用this調(diào)用。

public class ComplexCurrentThread extends Thread{
  public ComplexCurrentThread() {
    System.out.println("begin=========");
    System.out.println("Thread.currentThread().getName=" + Thread.currentThread().getName());
    System.out.println("this.getName()=" + this.getName());
    System.out.println("end===========");
  }
  @Override
  public void run() {
    super.run();
    System.out.println("run begin=======");
    System.out.println("Thread.currentThread().getName=" + Thread.currentThread().getName());
    System.out.println("this.getName()=" + this.getName());
    System.out.println("run end==========");
  }
}

測試代碼如下:

public class ComplexCurrentThreadTest extends TestCase {
  public void testRun() throws Exception {
    ComplexCurrentThread thread = new ComplexCurrentThread();
    thread.setName("byhieg");
    thread.start();
    Thread.sleep(3000);
  }
}

結(jié)果如下:

begin=========
Thread.currentThread().getName=main
this.getName()=Thread-0
end===========
run begin=======
Thread.currentThread().getName=byhieg
this.getName()=byhieg
run end==========

首先在創(chuàng)建對象的時候,構(gòu)造器還是被main線程所執(zhí)行,所以Thread.currentThread()得到的就是Main線程的名字,但是this方法指的是調(diào)用方法的那個對象,也就是ComplexCurrentThread的線程信息,還沒有setName,所以是默認的名字。然后run方法無論是Thread.currentThread()還是this返回的都是設(shè)置了byhieg名字的線程信息。

所以Thread.currentThread指的是具體執(zhí)行這個代碼塊的線程信息。構(gòu)造器是main執(zhí)行的,而run方法則是哪個線程start,哪個線程執(zhí)行run。這么看來,this能得到的信息是不準確的,因為如果我們在run中執(zhí)行了this.getName(),但是run方法卻是由另一個線程start的,我們是無法通過this.getName得到運行run方法的新城的信息的。而且只有繼承了Thread的類才能有g(shù)etName等方法,這對于Java沒有多繼承的特性語言來說,是個災難。所有后面凡是要得到線程的信息,我們都用Thread.currentThread()來調(diào)用API。

得到線程的ID

調(diào)用getID取得線程的唯一標識。這個和上面的getName用法一致,沒什么好說的,可以直接看ExampleIdThread和他的測試類ExampleIdThreadTest。

判斷線程是否存活

方法isAlive()的作用是測試線程是否處于活動狀態(tài)。所謂活動狀態(tài),就是線程已經(jīng)啟動但是沒有終止。即該線程start之后,被認為是存活的。

我們看一下具體的例子:

public class AliveThread extends Thread{
  @Override
  public void run() {
    super.run();
    System.out.println("run方法中是否存活" + "  " + Thread.currentThread().isAlive());
  }
}

測試方法如下:

public class AliveThreadTest extends TestCase {
  public void testRun() throws Exception {
    AliveThread thread = new AliveThread();
    System.out.println("begin == " + thread.isAlive());
    thread.start();
    Thread.sleep(1000);
    System.out.println("end ==" + thread.isAlive());
    Thread.sleep(3000);
  }
}

結(jié)果如下:

begin == false
run方法中是否存活  true
end ==false

我們可以發(fā)現(xiàn)在start之前,該線程被認為是沒有存活,然后run的時候,是存活的,等run方法執(zhí)行完,又被認為是不存活的。

如何停止線程

判斷線程是否終止

JDK提供了一些方法來判斷線程是否終止 —— isInterrupted()和interrupted()

停止線程的方式

這個是得到線程信息中比較重要的一個方法了,因為這個和終止線程的方法相關(guān)聯(lián)。先說一下終止線程的幾種方式:

  1. 等待run方法執(zhí)行完
  2. 線程對象調(diào)用stop()
  3. 線程對象調(diào)用interrupt(),在該線程的run方法中判斷是否終止,拋出一個終止異常終止。
  4. 線程對象調(diào)用interrupt(),在該線程的run方法中判斷是否終止,以return語句結(jié)束。

第一種就不說了,第二種stop()方法已經(jīng)廢棄了,因為可能會產(chǎn)生如下原因:

  1. 強制結(jié)束線程,該線程應該做的清理工作,無法完成。
  2. 強制結(jié)束線程,該線程已操作的加鎖對象強制解鎖,造成數(shù)據(jù)不一致。

具體的例子可以看StopLockThread以及他的測試類StopLockThreadTest

第三種,是目前推薦的終止方法,調(diào)用interrupt,然后在run方法中判斷是否終止。判斷終止的方式有兩種,一種是Thread類的靜態(tài)方法interrupted(),另一種是Thread的成員方法isInterrupted()。這兩個方法是有所區(qū)別的,第一個方法是會自動重置狀態(tài)的,如果連續(xù)兩次調(diào)用interrupted(),第一次如果是false,第二次一定是true。而isInterrupted()是不會的。

例子如下:

public class ExampleInterruptThread extends Thread{
  @Override
  public void run() {
    super.run();
    try{
      for(int i = 0 ; i < 50000000 ; i++){
        if (interrupted()){
          System.out.println("已經(jīng)是停止狀態(tài),我要退出了");
          throw new InterruptedException("停止.......");
        }
        System.out.println("i=" + (i + 1));
      }
    }catch (InterruptedException e){
      System.out.println("順利停止");
    }
  }
}

測試的代碼如下:

public class ExampleInterruptThreadTest extends TestCase {
  public void testRun() throws Exception {
    ExampleInterruptThread thread = new ExampleInterruptThread();
    thread.start();
    Thread.sleep(1000);
    thread.interrupt();
  }
}

第四種方法和第三種一樣,唯一的區(qū)別就是將上面的代碼中的拋出異常換成return,個人還是喜歡拋出異常,這里處理的形式就比較多,比如打印信息,處理資源關(guān)閉或者捕捉之后再重新向上層拋出。

注意一點,我們上面拋出的異常是InterruptedException,這里簡單說一下可能產(chǎn)生這個異常的原因,在原有線程sleep的情況下,調(diào)用interrupt終止線程,或者先終止線程,再讓線程sleep。

如何暫停線程

在JDK中提供了以下兩個方法用來暫停線程和恢復線程。

  • suspend()——暫停線程
  • resume()——恢復線程

這兩個方法和stop方法一樣是被廢棄的方法,其用法和stop一樣,暴力的暫停線程和恢復線程。這兩個方法之所以是廢棄的主要由以下兩個原因:

  1. 線程持有鎖定的公共資源的情況下,一旦被暫停,則公共資源無法被其他線程所持有。
  2. 線程強制暫停,導致該線程執(zhí)行的操作沒有執(zhí)行完全,這時訪問該線程的數(shù)據(jù)會出現(xiàn)數(shù)據(jù)不一致。

線程的一些其他用法

線程的其他的一些基礎(chǔ)用法如下:

  1. 線程讓步
  2. 設(shè)置線程的優(yōu)先級
  3. 守護線程

線程讓步

JDK提供yield()方法來讓線程放棄當前的CPU資源,將它讓給其他的任務去占用CPU時間,但是這也是隨機的事情,有可能剛放棄資源,又馬上占用時間片了。

具體的例子可以參考ExampleYieldThread以及他的測試類ExampleYieldThreadTest

設(shè)置線程的優(yōu)先級

我們可以設(shè)置線程的優(yōu)先級來讓CPU盡可能的將執(zhí)行的資源給優(yōu)先級高的線程。Java設(shè)置了1-10這10個優(yōu)先級,又有三個靜態(tài)變量來提供三個優(yōu)先級:

  /**
   * The minimum priority that a thread can have.
   */
  public final static int MIN_PRIORITY = 1;

  /**
   * The default priority that is assigned to a thread.
   */
  public final static int NORM_PRIORITY = 5;

  /**
   * The maximum priority that a thread can have.
   */
  public final static int MAX_PRIORITY = 10;

我們可以通過setPriority來設(shè)置線程的優(yōu)先級,可以直接傳入上訴三個靜態(tài)變量,也可以直接傳入1-10的數(shù)字。設(shè)置后線程就會有不同的優(yōu)先級。如果我們不設(shè)置優(yōu)先級,會是什么情況?

線程的優(yōu)先級是有繼承的特性,如果我們在A線程中啟動了B線程,則AB具有相同的優(yōu)先級。一般我們在main線程中啟動線程,就和main線程有一致的優(yōu)先級。main線程的優(yōu)先級默認是5。

下面說一下優(yōu)先級的一些規(guī)則:

  • 優(yōu)先級高的線程一般會比優(yōu)先級低的線程獲得更多的CPU資源,但是不代表優(yōu)先級高的任務一定先于優(yōu)先級低的任務先執(zhí)行完。因為不同優(yōu)先級的線程中run方法內(nèi)容可能不一樣。
  • 優(yōu)先級高的線程一定會比優(yōu)先級低的線程執(zhí)行的快。如果兩個線程是一樣的run方法,但是優(yōu)先級不一樣,確實優(yōu)先級高的線程先執(zhí)行完。

線程守護

JDK中提供setDaemon的方法來設(shè)置一個線程變成守護線程。守護線程的特點是其他非守護線程執(zhí)行完,守護線程就自動銷毀,典型的例子是GC回收器。

具體可以看ExampleDaemonThread和ExampleDaemonThreadTest。

總結(jié)

這篇文章主要總結(jié)了Java線程的一些基本的用法,關(guān)于線程安全,同步的知識,放到了第二篇。

以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!

相關(guān)文章

  • java獲取指定開始時間與結(jié)束時間之間的所有日期

    java獲取指定開始時間與結(jié)束時間之間的所有日期

    這篇文章主要為大家詳細介紹了java獲取指定開始時間與結(jié)束時間之間的所有日期,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-05-05
  • spring?boot整合mongo查詢converter異常排查記錄

    spring?boot整合mongo查詢converter異常排查記錄

    這篇文章主要為大家介紹了spring?boot整合mongo查詢時拋出converter異常的排查解決記錄,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-03-03
  • java equals函數(shù)用法詳解

    java equals函數(shù)用法詳解

    java 中equals函數(shù)的使用方法是廣大java愛好者所關(guān)心的一個話題,本文將詳細介紹其使用方法,需要了解的朋友可以參考下
    2012-11-11
  • Java日期時間使用方法匯總

    Java日期時間使用方法匯總

    這篇文章主要針對Java日期時間使用方法進行匯總,感興趣的朋友可以參考一下
    2016-03-03
  • 一文徹底搞懂Java日期時間類詳解

    一文徹底搞懂Java日期時間類詳解

    這篇文章主要給大家介紹了關(guān)于Java日期時間類的相關(guān)資料,Calendar類的功能要比Date類強大很多,可以方便的進行日期的計算,獲取日期中的信息時考慮了時區(qū)等問題,需要的朋友可以參考下
    2023-10-10
  • Mybatis插入Oracle數(shù)據(jù)庫日期型數(shù)據(jù)過程解析

    Mybatis插入Oracle數(shù)據(jù)庫日期型數(shù)據(jù)過程解析

    這篇文章主要介紹了Mybatis插入Oracle數(shù)據(jù)庫日期型數(shù)據(jù)過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-09-09
  • Spring啟動過程源碼分析及簡介

    Spring啟動過程源碼分析及簡介

    本文是通過AnnotationConfigApplicationContext讀取配置類來一步一步去了解Spring的啟動過程。本文重點給大家介紹Spring啟動過程源碼分析及基本概念,感興趣的朋友一起看看吧
    2021-10-10
  • Nacos配置中心搭建及動態(tài)刷新配置及踩坑記錄

    Nacos配置中心搭建及動態(tài)刷新配置及踩坑記錄

    這篇文章主要介紹了Nacos配置中心搭建及動態(tài)刷新配置及踩坑記錄,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • java使用Apache工具集實現(xiàn)ftp文件傳輸代碼詳解

    java使用Apache工具集實現(xiàn)ftp文件傳輸代碼詳解

    這篇文章主要介紹了java使用Apache工具集實現(xiàn)ftp文件傳輸代碼詳解,分享了詳細連接ftp server和上傳文件,下載文件的代碼,以及結(jié)果展示,具有一定借鑒價值,需要的朋友可以參考下。
    2017-12-12
  • Spring 異常處理的各種姿勢總結(jié)

    Spring 異常處理的各種姿勢總結(jié)

    這篇文章主要介紹了Spring 異常處理,總結(jié)分析了Spring 異常處理的各種常見操作技巧與相關(guān)使用注意事項,需要的朋友可以參考下
    2020-05-05

最新評論