最全java多線程學習總結1--線程基礎

  《java 核心技術》這本書真的不錯,知識點很全面,翻譯質量也還不錯,本系列博文是對該書中併發章節的一個總結。html

什麼是線程

  官方解釋:線程是操做系統可以進行運算調度的最小單位,包含於進程之中,是進程中的實際運做單位。也就是說線程是代碼運行的載體,咱們所編寫的代碼都是在線程上跑的,以一個最簡單的 hellowWorld 爲例:java

public class Main {

    public static void main(String[] args) {
        System.out.println("Hello World!");
        System.out.println("當前線程名爲:"+Thread.currentThread().getName());
        System.out.println("當前線程id爲:"+Thread.currentThread().getId());
    }
}

<!-- more -->git

結果爲:github

Hello World!
當前線程名爲:main
當前線程id爲:1

在程序運行時默認會建立一個主線程來執行代碼,線程名爲:main,線程 id 爲 1多線程

什麼是多線程

  顧名思義就是多個線程同時運行,提升程序執行速度。單個線程一次只能作一件事,想要提升執行效率有兩種途徑:併發

  • 異步。由於大多數時候線程都不是時刻在進行計算,都是在等待 io 操做,那麼就能夠將等待時間利用起來以提升線程的利用率。這裏不作過多討論,想要進一步瞭解異步的能夠學習 Node.js(原生支持異步)
  • 多線程。一個線程一次只能作一件事,那麼多個線程就能同時作多件事了,經過增大線程數來提升執行速度。

如何建立線程

  建立線程有兩種方法dom

  • 繼承 Thread 類
  • 實現 runnable 接口

繼承 Thread 類

  不推薦本方式來建立線程,緣由顯而易見:java 不支持多繼承,若是繼承了 Thread 類就不能再繼承其餘類了。異步

  使用繼承方式建立線程代碼以下:ide

public class CustomThreadExtendThread extends Thread{

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        long threadId = Thread.currentThread().getId();
        System.out.println("建立線程名爲:"+threadName+",id爲:"+threadId);
    }

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

實現 runnable 接口

  實現接口來建立線程是目前推薦的一種方式,緣由也很簡單:一個類能夠實現多個接口。實現 Runnable 接口並不影響實現類再去實現其餘接口。學習

  使用實現接口方式建立線程代碼以下:

public class CustomThreadImplementInterface implements Runnable {
    @Override
    public void run() {
        Thread.currentThread().setName(((Double) Math.random()).toString());
        String threadName = Thread.currentThread().getName();
        long threadId = Thread.currentThread().getId();
        System.out.println("建立線程名爲:" + threadName + ",id爲:" + threadId);
    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(new CustomThreadImplementInterface());
        Thread thread2 = new Thread(new CustomThreadExtendThread());
        thread1.start();
        thread2.start();

        //使用lambda表達式,讓建立線程更簡單
        new Thread(() -> {
            System.out.println("建立了一個新線程");
        }).start();
    }
}

  經過查看 Thread 源碼能夠看到 Thread 類也是 Runnable 接口的一個實現類。

PS:後續代碼所有使用 runnable 建立線程

線程狀態

  上面只是演示了線程的建立,如今來詳細瞭解線程的狀態。在 java 規範中,線程能夠有如下 6 種狀態:

  • New(新建立)
  • Runnable(可運行)
  • Blocked(阻塞)
  • Waiting(等待)
  • Timed waiting(計時等待)
  • Terminated(被終止)

新建立線程

  當使用 new 操做符建立一個線程時,如 new Thread(r),線程還未開始運行,就屬於新建立狀態。

可運行線程

  一旦調用 Thread 類的 start 方法,線程就處於可運行狀態。

爲何要叫運行狀態?

  由於 Java 的規範中並無將正在 CPU 上運行定義爲一個單獨的狀態。所以處於可運行狀態的線程可能正在運行,也可能沒有運行,取決於 CPU 的調度策略。

被阻塞線程和等待線程

  當線程處於阻塞或等待狀態時,不運行任何代碼且消耗最少的資源。直到從新運行。有以下幾種途徑讓線程進入阻塞或等待狀態:

  • 當一個線程試圖獲取一個內部的對象鎖,而該鎖被其餘線程持有
  • 當線程等待另外一個線程通知調度器一個條件時,進入等待狀態。好比調用 Object.wait 或 Thread.join 方法,或等待 java.util.concurrent 庫中的 Lock 或 Condition 時。
  • 當調用計時等待方法時。好比 Thread.sleep,Object.wait,Thread.join,Lock.tryLock 以及 Condition.await

被終止的線程

  線程可由如下兩種辦法進入終止狀態:

  • run 方法的結束而天然死亡
  • 未捕獲異常停止了 run 方法而意外死亡

注意: 調用線程的 stop 方法也能夠終止線程,可是這個方法已經被棄用,最好不要使用。

線程屬性

  線程有各類屬性:優先級,守護線程,線程組以及處理未捕獲異常處理器。

線程優先級

  java 中,每一個線程都有一個優先級。默認狀況下,線程繼承父線程優先級。也能夠調用setPriority方法指定優先級。優先級範圍:1(MIN_PRIORITY)-10(MAX_PRIORITY).NORM_PRIORITY 爲 5,這些常量定義在 Thread 類中.

注意: 線程優先級時高度依賴於系統的,所以當 java 線程優先級映射到宿主機平臺的優先級時,優先級個數可能會變少或者變成 0.好比,Windows 中有 7 個優先級,java 線程映射時部分優先級將會映射到相同的操做系統優先級上。Oracle 爲 Linux 編寫的 java 虛擬機中,忽略了線程的優先級,全部 java 線程都有相同的優先級。不要編寫依賴優先級的代碼

守護線程

  經過調用Thread.setDaemon(true)將一個線程轉換爲守護線程。守護線程惟一的用戶是爲其餘線程提供服務,好比計時線程,定時發送計時信號給其餘線程。所以當虛擬機中只有守護線程時,虛擬機就會關閉退出。不要在守護線程中訪問任何資源,處理任何業務邏輯

未捕獲異常處理器

  線程的 run 方法不能拋出任何受查異常,非受查異常會致使線程終止,除了 try/catch 捕獲異常外,還能夠經過未捕獲異常處理器來處理異常。異常處理器須要實現Thread.UncaughtExceptionHandler接口。

  可使用線程示例的setUncaughtExceptionHandler()方法爲某個線程設置處理器,也可以使用Thread.setDefaultUncaughtExceptionHandler()爲全部線程設置默認處理器,代碼以下:

public class CustomExceptionHandler implements Thread.UncaughtExceptionHandler {

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("捕獲到線程"+t.getName()+",異常:" + e.getMessage());
        e.printStackTrace();
    }

    public static void main(String[] args) {
        Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler());
        new Thread(() -> {
            throw new RuntimeException("test");
        }).start();
    }
}

  若是不設置默認處理器且不爲獨立的線程設置處理器,那麼該線程的處理器就爲該線程的線程組對象--ThreadGroup(由於線程組對象實現了Thread.UncaughtExceptionHandler接口)。

本篇所用所有代碼:github

本篇原創發佈於:https://www.tapme.top/blog/detail/2019-04-08-20-52

原文出處:https://www.cnblogs.com/wuyoucao/p/11100889.html

相關文章
相關標籤/搜索