Java多線程學習筆記

做者:Greyhtml

原文地址:Java多線程學習筆記java

源碼: Githubgit

什麼是程序,進程和線程?

  • 程序是計算機的可執行文件
  • 進程是計算機資源分配的基本單位
  • 線程是資源調度執行的基本單位
    • 一個程序裏面不一樣的執行路徑
    • 多個線程共享進程中的資源

線程和進程的關係

線程就是輕量級進程,是程序執行的最小單位。github

多進程的方式也能夠實現併發,爲何咱們要使用多線程?編程

  1. 共享資源在線程間的通訊比較容易。
  2. 線程開銷更小。

進程和線程的區別?

  • 進程是一個獨立的運行環境,而線程是在進程中執行的一個任務。他們兩個本質的區別是是否單獨佔有內存地址空間及其它系統資源(好比I/O)。
  • 進程單獨佔有必定的內存地址空間,因此進程間存在內存隔離,數據是分開的,數據共享複雜可是同步簡單,各個進程之間互不干擾;而線程共享所屬進程佔有的內存地址空間和資源,數據共享簡單,可是同步複雜。
  • 進程單獨佔有必定的內存地址空間,一個進程出現問題不會影響其餘進程,不影響主程序的穩定性,可靠性高;一個線程崩潰可能影響整個程序的穩定性,可靠性較低。
  • 進程單獨佔有必定的內存地址空間,進程的建立和銷燬不只須要保存寄存器和棧信息,還須要資源的分配回收以及頁調度,開銷較大;線程只須要保存寄存器和棧信息,開銷較小。
  • 進程是操做系統進行資源分配的基本單位,而線程是操做系統進行調度的基本單位,即CPU分配時間的單位。

什麼是線程切換?

從底層角度上看,CPU主要由以下三部分組成,分別是:網絡

  • ALU: 計算單元
  • Registers: 寄存器組
  • PC:存儲到底執行到哪條指令

T1線程在執行的時候,將T1線程的指令放在PC,數據放在Registers,假設此時要切換成T2線程,T1線程的指令和數據放cache,而後把T2線程的指令放PC,數據放Registers,執行T2線程便可。多線程

以上的整個過程是經過操做系統來調度的,且線程的調度是要消耗資源的,因此,線程不是設置越多越好。併發

單核CPU設定多線程是否有意義?

有意義,由於線程的操做中可能有不消耗CPU的操做,好比:等待網絡的傳輸,或者線程sleep,此時就可讓出CPU去執行其餘線程。能夠充分利用CPU資源。ide

  • CPU密集型
  • IO密集型

線程數量是否是設置的越大越好?

不是,由於線程切換要消耗資源。高併發

示例:
單線程和多線程來累加1億個數。-> CountSum.java

工做線程數(線程池中線程數量)設多少合適?

  • 和CPU的核數有關

  • 最好是經過壓測來評估。經過profiler性能分析工具jProfiler,或者Arthas

  • 公式

N = Ncpu * Ucpu * (1 + W/C)

其中:

  • Ncpu是處理器的核的數目,能夠經過Runtime.getRuntime().availableProcessors() 獲得

  • Ucpu是指望的CPU利用率(該值應該介於0和1之間)

  • W/C是等待時間和計算時間的比率。

Java中建立線程的方式

  1. 繼承Thread類,重寫run方法
  2. 實現Runnable接口,實現run方法,這比方式1更好,由於一個類實現了Runnable之後,還能夠繼承其餘類
  3. 使用lambda表達式
  4. 經過線程池建立
  5. 經過Callable/Future建立(須要返回值的時候)

具體示例可見:HelloThread.java

線程狀態

  • NEW

線程剛剛建立,尚未啓動
即:剛剛New Thread的時候,尚未調用start方法時候,就是這個狀態

  • RUNNABLE

可運行狀態,由線程調度器能夠安排執行,包括如下兩種狀況:

  • READY
  • RUNNING

READY和RUNNING經過yield來切換

  • WAITING

等待被喚醒

  • TIMED_WAITING

隔一段時間後自動喚醒

  • BLOCKED

被阻塞,正在等待鎖
只有在synchronized的時候在會進入BLOCKED狀態

  • TERMINATED

線程執行完畢後,是這個狀態

線程狀態切換

java_thread_state

interrupt

  • interrupt()

打斷某個線程(設置標誌位)

  • isInterrupted()

查詢某線程是否被打斷過(查詢標誌位)

  • static interrrupted

查詢當前線程是否被打斷過,並重置打斷標誌位

示例代碼:ThreadInterrupt.java

如何結束一個線程

不推薦的方式

  • stop方法
  • suspend/resume方法

以上兩種方式都不建議使用, 由於會產生數據不一致的問題,由於會釋放全部的鎖。

優雅的方式

若是不依賴循環的具體次數或者中間狀態, 能夠經過設置標誌位的方式來控制

public class ThreadFinished {
    private static volatile boolean flag = true;
    public static void main(String[] args) throws InterruptedException {

        // 推薦方式:設置標誌位
        Thread t3 = new Thread(() -> {
            long i = 0L;
            while (flag) {
                i++;
            }
            System.out.println("count sum i = " + i);
        });
        t3.start();
        TimeUnit.SECONDS.sleep(1);
        flag = false;
    }
}

若是要依賴循環的具體次數或者中間狀態, 則能夠用interrupt方式

public class ThreadFinished {

    public static void main(String[] args) throws InterruptedException {
        // 推薦方式:使用interrupt
        Thread t4 = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {

            }
            System.out.println("t4 end");
        });
        t4.start();
        TimeUnit.SECONDS.sleep(1);
        t4.interrupt();
    }
}

示例代碼: ThreadFinished.java

參考資料

多線程與高併發-馬士兵

實戰Java高併發程序設計(第2版)

深刻淺出Java多線程

Java併發編程實戰

相關文章
相關標籤/搜索