Java併發3:線程

本文是根據《Java併發編程的藝術》以及以前的文章 https://juejin.im/post/5c09dbed51882539c60cfd63 和 https://juejin.im/post/5c0cff456fb9a049de6d30d6 共同整理而來。java

進程和線程

進程

進程是操做系統結構的基礎;是一次程序的執行;是一個程序及其數據在處理機上順序執行時所發生的活動;是程序在一個數據集合上運行的過程,它是從系統進行資源分配和調度的一個獨立單位。編程

線程

線程是一個比進程更小的執行單位,能夠理解成是在進程中獨立運行的子任務。一個進程在執行過程當中能夠產生多個線程,同類的多個線程共享同一塊內存空間和一組系統資源,因此係統在產生一個線程,或是在各個線程之間作切換時,負擔比進程小得多。bash

多線程

多線程就是指多個線程同時運行或交替運行。單核CPU是順序執行,也即交替運行。多核CPU,由於每一個CPU有本身的運算器,因此在多個CPU能夠同時運行。多線程

爲何要使用多線程

  1. 更多的處理器核心,使得多線程能分配到多個處理器核心,減小了程序的處理時間,提高了效率
  2. 更快的響應時間:將數據一致性不強的操做派發給不一樣的線程,從而縮短了響應時間
  3. 更好的編程模型

相關概念

線程優先級

Java線程中,經過整形變量priority來控制優先級,範圍從1-10,建立線程時能夠設置,默認爲5。在不一樣的JVM和操做系統上,線程規劃存在差別,有些操做系統甚至會忽略對線程優先級的設定。併發

同步和異步

同步方法調用開始後,調用者必須等待,直到方法調用返回後,才能繼續後序的行爲。能夠理解爲,排隊執行。異步

異步方法調用像是一個消息傳遞,當一個異步過程調用發出後,調用者能夠繼續後序的操做,可是調用者不能馬上獲得結果。實際處理這個調用的部件在完成後,經過狀態、通知和回調來通知調用者。多線程是異步的,其調用時機是隨機的。ide

併發和並行

併發是指一個處理器同時處理多個任務。並行是指多個處理器或者是多核的處理器同時處理多個不一樣的任務。函數

如上圖所示,併發是邏輯上是同時發生。並行是物理上的同時發生。

多線程在單核CPU的話是順序執行,也就是交替運行(併發)。多核CPU的話,由於每一個CPU有本身的運算器,因此在多個CPU中能夠同時運行(並行)。post

阻塞與非阻塞

阻塞是指調用結果返回以前,當前線程會被掛起,只有在獲得結果後才返回。非阻塞指不能馬上獲得結果以前,該函數不會阻塞當前線程,而會馬上返回。spa

守護線程

Java中有兩種線程,一種是用戶線程,另一種是守護線程。當進程中不存在非守護線程了,守護線程自動銷燬,java虛擬機會退出。典型的守護線程就是垃圾回收線程。只要當前JVM實例中存在任何一個非守護線程沒有結束,守護線程就在工做,只有當最後一個非守護線程結束,守護線程才隨着JVM一同結束工做。當JVM退出時,守護線程中的finally塊不必定會執行,不能依靠finally塊中的內容確保執行關閉或者清理資源的邏輯。

線程狀態轉換

  • 新建(New):建立後還沒有啓動。

  • 可運行(Runnable):線程對象建立後,在調用它的start()方法,系統爲此線程分配CPU資源,使其處於可運行狀態,這是一個準備運行的狀態。

    • 調用sleep()方法後通過的時間超過了指定的休眠時間。
    • 線程調用的阻塞IO返回,阻塞方法執行完畢。
    • 線程得到了試圖同步的監視器
    • 線程正在等待通知,且其餘線程發出了通知
    • 處於掛起狀態的線程調用了resume恢復方法。
  • 運行(Running):在Runnable狀態下的線程得到了CPU時間片,執行程序代碼。

  • 阻塞(Blocking):線程由於某種緣由放棄了CPU使用權,讓出了CPU時間片,暫時中止運行。包括以下五種狀況:

    • 線程調用sleep()方法
    • 線程調用了阻塞IO方法,在該方法返回前,被阻塞
    • 線程試圖得到同步鎖,該鎖被其餘線程持有(同步阻塞)
    • 線程等待通知(等待阻塞)
    • 程序調用了suspend方法掛起(避免該方法,易死鎖)
  • 死亡(Dead):線程run()、main()方法執行結束,或者因異常退出了run()方法,則該線程結束生命週期。死亡的線程不可再次復生。

線程建立及啓動

在Java中,實現多線程編程的方式主要有兩種,一種是繼承Thread類,一種是實現Runnable接口。

在Java源碼中,Thread類實現了Runnable接口,它們之間具備多態關係。使用這兩種方式建立的線程在工做時性質是同樣的,沒有本質區別。

線程對象在初始化完成後,調用 start() 方法啓動這個線程。其含義是當前線程(啓動子線程的線程)告知JVM,只要線程規劃器空閒,當即啓動調用 start() 方法的線程。

示例代碼參考:https://juejin.im/post/5c09dbed51882539c60cfd63

線程中斷

中斷能夠理解爲線程的一個標誌位屬性,表示一個運行中的線程是否被其餘線程進行了中斷操做。線程中斷是一種用於中止線程的協做機制,線程能夠經過這種機制來通知另外一個線程,告訴它在合適的或者可能的狀況下中止當前工做。

使用interrupt()方法將中斷狀態設置爲ture;使用isInterrupted()判斷線程的中斷狀態;使用Thread.interrupted()判斷當前線程的中斷狀態,並將當前線程的中斷狀態設置爲false。

參考:https://juejin.im/post/5c1c51476fb9a049b221d97c

線程通訊

等待/通知機制

參考:https://juejin.im/post/5c0cff456fb9a049de6d30d6

等待通知的經典範式:

等待方:1.獲取對象鎖;2.若是條件不知足,調用對象的 wait() 方法;3.被通知後檢查條件,條件知足執行對應的邏輯。

synchronized(對象){
    while(條件不知足){
        對象.wait();
    }
    處理邏輯
}
複製代碼

通知方:1.獲取對象鎖;2.改變條件;3.通知等待該對象的進程

synchronized(對象){
    改變條件
    對象.notifyAll();
}
複製代碼

管道流

管道流用於線程之間的數據傳輸,其媒介爲內存。 一個線程發送數據到輸出管道,另外一個線程從輸入管道中讀取數據。經過使用管道,實現不一樣線程間的通訊,而無須藉助相似臨時文件之類的東西。 JDK中提供了四個類:PipedInputStream,PipedOutputStream;PipedReader,PipedWriter。

示例代碼: https://juejin.im/post/5c0e16b16fb9a049df23e62a

join()方法

在線程A的代碼中執行了線程B threadB.join(),含義是線程A等待線程B終止以後才繼續進行線程該代碼以後的工做。join() 方法還能夠傳入參數如join(long mills)以及join(long mills,int nanos)的超時方法,表示線程在給定時間內沒有終止,將從中返回。

實例代碼: https://juejin.im/post/5c0e1de6e51d451dd71e2a74

ThreadLocal

ThreadLocal 是存放線程變量的變量,是一個以ThreadLocal對象爲key,任意對象爲value的存儲結構。

public class Profiler {
    private static final ThreadLocal<Long> TIME_TL=new ThreadLocal<Long>(){
        @Override
        protected Long initialValue() {
            return System.currentTimeMillis();
        }
    };
    private static final ThreadLocal<String> STR_TL=new ThreadLocal<String>(){
        @Override
        protected String initialValue() {
            return "hello";
        }
    };

    public static final void begin(){
        TIME_TL.set(System.currentTimeMillis());
    }

    public static final long end(){
        return System.currentTimeMillis()-TIME_TL.get();
    }

    public static void main(String[] args) throws InterruptedException {
        Profiler.begin();
        TimeUnit.SECONDS.sleep(1);
        System.out.println("Cost "+Profiler.end()+" mills");
        System.out.println(STR_TL.get());
    }
}
複製代碼

運行結果:

Cost 1001 mills
hello
複製代碼
相關文章
相關標籤/搜索