1. 程式人生 > 實用技巧 >多執行緒與高併發學習筆記(持續更新中)

多執行緒與高併發學習筆記(持續更新中)

一、 基礎概念

什麼是執行緒

  • 什麼是程序?什麼是執行緒?
    程序是系統中正在執行的一個程式,程式一旦執行就是程序。

程序可以看成程式執行的一個例項。程序是系統資源分配的獨立實體,每個程序都擁有獨立的地址空間。一個程序無法訪問另一個程序的變數和資料結構,如果想讓一個程序訪問另一個程序的資源,需要使用程序間通訊,比如管道,檔案,套接字等。

一個程序可以擁有多個執行緒,每個執行緒使用其所屬程序的棧空間。執行緒與程序的一個主要區別是,統一程序內的一個主要區別是,同一程序內的多個執行緒會共享部分狀態,多個執行緒可以讀寫同一塊記憶體(一個程序無法直接訪問另一程序的記憶體)。同時,每個執行緒還擁有自己的暫存器和棧,其他執行緒可以讀寫這些棧記憶體。

執行緒是程序的一個實體,是程序的一條執行路徑。

執行緒是程序的一個特定執行路徑。當一個執行緒修改了程序的資源,它的兄弟執行緒可以立即看到這種變化。

  • 程序和執行緒的區別體現在以下幾個方面:
    1.地址空間和其他資源(如開啟檔案):程序間相互獨立,同一程序的各執行緒間共享。某程序內的執行緒在其他程序內不可見。

2.通訊:程序間通訊IPC(管道,訊號量,共享記憶體,訊息佇列),執行緒間可以直接獨寫程序資料段(如全域性變數)來程序通訊——需要程序同步和互斥手段的輔助,以保證資料的一致性。

3.排程和切換:執行緒上下文切換比程序上下文切換快得多。

4.在多執行緒OS中,程序不是一個可執行的實體。

  • 程序和執行緒的選擇取決以下幾點:
    1.需要頻繁建立銷燬的優先使用執行緒;因為對程序來說建立和銷燬一個程序的代價是很大的。

2.執行緒的切換速度快,所以在需要大量計算,切換頻繁時使用執行緒,還有耗時的操作時用使用執行緒可提高應用程式的響應。

3.因為對CPU系統的效率使用上執行緒更佔優勢,所以可能要發展到多機分佈的用程序,多核分佈用執行緒。

4.並行操作時用執行緒,如C/S架構的伺服器端併發執行緒響應使用者的請求。

5.需要更穩定安全時,適合選擇程序;需要速度時,選擇執行緒更好。

執行緒的實現

繼承Thread類或者實現Runnable介面

package com.mashibing.juc.c_000;

public class T02_HowToCreateThread {
    static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("Hello MyThread!");
        }
    }

    static class MyRun implements Runnable {
        @Override
        public void run() {
            System.out.println("Hello MyRun!");
        }
    }

    public static void main(String[] args) {
        new MyThread().start();
        new Thread(new MyRun()).start();
        new Thread(()->{
            System.out.println("Hello Lambda!");
        }).start();
    }

}

//請你告訴我啟動執行緒的三種方式 1:Thread 2: Runnable 3:Executors.newCachedThrad
  • yield
    Thread.yield()方法作用是:暫停當前正在執行的執行緒物件(及放棄當前擁有的cup資源),並執行其他執行緒。

yield()做的是讓當前執行執行緒回到可執行狀態,以允許具有相同優先順序的其他執行緒獲得執行機會。因此,使用yield()的
目的是讓相同優先順序的執行緒之間能適當的輪轉執行。但是,實際中無法保證yield()達到讓步目的,因為讓步的執行緒還有可能被
執行緒排程程式再次選中。

結論:yield()從未導致執行緒轉到等待/睡眠/阻塞狀態。在大多數情況下,yield()將導致執行緒從執行狀態轉到可執行狀態,但有可能沒有效果

  • join
    大白話:就是誰呼叫這個方法,就讓呼叫此方法的執行緒進入阻塞狀態,等待我執行完畢之後,再往下執行;

那麼我們再來看上面那段加了join()的程式碼,首先開啟執行緒A,緊接著執行緒A呼叫了join()方法進入阻塞狀態,那麼執行緒必須等待執行緒A執行結束之後再往下執行,執行緒A執行完畢,執行緒B開啟,進入睡眠,主執行緒執行,執行緒B睡眠結束,執行;
知乎-關於join的解釋

執行緒狀態

  • 新建狀態:
    使用 new 關鍵字和 Thread 類或其子類建立一個執行緒物件後,該執行緒物件就處於新建狀態。它保持這個狀態直到程式 start() 這個執行緒。
  • 就緒狀態:
    當執行緒物件呼叫了start()方法之後,該執行緒就進入就緒狀態。就緒狀態的執行緒處於就緒佇列中,要等待JVM裡執行緒排程器的排程。
  • 執行狀態:
    如果就緒狀態的執行緒獲取 CPU 資源,就可以執行 run(),此時執行緒便處於執行狀態。處於執行狀態的執行緒最為複雜,它可以變為阻塞狀態、就緒狀態和死亡狀態。
  • 阻塞狀態:
    如果一個執行緒執行了sleep(睡眠)、suspend(掛起)等方法,失去所佔用資源之後,該執行緒就從執行狀態進入阻塞狀態。在睡眠時間已到或獲得裝置資源後可以重新進入- - 就緒狀態。可以分為三種:
    • 等待阻塞:執行狀態中的執行緒執行 wait() 方法,使執行緒進入到等待阻塞狀態。
    • 同步阻塞:執行緒在獲取 synchronized同步鎖失敗(因為同步鎖被其他執行緒佔用)。
    • 其他阻塞:通過呼叫執行緒的 sleep() 或 join() 發出了 I/O請求時,執行緒就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待執行緒終止或超時,或者 I/O 處理完畢,執行緒重新轉入就緒狀態。
  • 死亡狀態:
    一個執行狀態的執行緒完成任務或者其他終止條件發生時,該執行緒就切換到終止狀態。

執行緒同步

  • synchronized 程式碼塊
  • synchronized方法
  • 從JDK1.5開始的Lock介面替代synchronized關鍵字

sychronized 鎖升級

https://blog.csdn.net/tongdanping/article/details/79647337

sychronized 同步方法與非同步方法

sychronized 鎖重入

異常與鎖

volatile

AtomicXXX

wait notify