1. 程式人生 > >java線程基礎梳理

java線程基礎梳理

邏輯 yield 暫時 category new t create 繼承 另一個 demo

java線程

概述
  • 進程:運行時概念,運行的應用程序,進程間不能共享內存
  • 線程:應用程序內並發執行的代碼段,可以共享堆內存和方法區內存,而棧內存是獨立的。
  • 並發理解:在單核機器上,從微觀角度來看,一段時間內cup只能執行一個任務,但是因為cup在只執行一段代碼段的時候大部分的時間是處於等待程序的,所以可以再開幾條程序,然後通過輪詢機制,讓cpu執行多個進程,從宏觀角度來看就是所謂的並發。如果機器是多核,那麽就是真正的並發。

    線程調度模型
  • 線程的調度模型分為: 分時調度模型和搶占式調度模型,Java使用搶占式調度模型
    • 分時調度模型: 所有線程輪流使用CPU的使用權,平均分配每個線程占用CPU的時間片
    • 搶占式調度模型: 優先讓優先級高的線程使用CPU,如果線程的優先級相同,那麽會隨機選擇一個,優先級高的線程獲取的CPU時間片相對多一些.
  • 線 程 優 先 級 主 要 分 三 種 : MAX_PRIORITY( 最 高 級 );MIN_PRIORITY ( 最 低 級 )NORM_PRIORITY(標準)默認

    java 程序的運行原理?
  • java 命令會啟動 java 虛擬機,啟動 JVM,等於啟動了一個應用程序,表示啟動了一個進程。該進程會自動啟動一個“主線程”,然後主線程去調用某個類的 main 方法。所以 main方法運行在主線程中。在此之前的所有程序都是單線程的。
  • 一個棧就是一個線程,所謂的並發就是多個不同的棧。

    線程創建
  • 繼承Thread
    • 線程編寫要繼承 java.lang.Thread
    • 重寫run方法
    • 調用的時候調用線程類的start()方法,run方法是讓cpu調用的。所以start()方法調用後run方法不會立刻調用。
  • 實現runable接口
class CreateRunnable implements Runnable {
    @Override
    publicvoid run() {
        for (inti = 0; i< 10; i++) {
            System.out.println("i:" + i);
        }
    }

}
public class ThreadDemo2 {
    publicstaticvoid main(String[] args) {
        System.out.println("-----多線程創建開始-----");
        // 1.創建一個線程
        CreateRunnable createThread = new CreateRunnable();
        // 2.開始執行線程 註意 開啟線程不是調用run方法,而是start方法
        System.out.println("-----多線程創建啟動-----");
        Thread thread = new Thread(createThread);
        thread.start();

    }
}
  • 使用匿名內部調用
System.out.println("-----多線程創建開始-----");
         Thread thread = new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i< 10; i++) {
                    System.out.println("i:" + i);
                }
            }
        });
         thread.start();
         System.out.println("-----多線程創建結束-----");
線程分類
  • 主線程、子線程、GC線程
    • 一個進程中肯定要有主線程。一般是用main函數來創建
    • java主線程結束是不影響子線程的。
    • GC線程是守護線程。
    • 守護線程隨著主線程的銷毀而銷毀。
    • 在一個線程start前用thread.setDaemon(true)可以將用戶線程變成守護線程。
  • 守護線程和用戶線程
    • 從線程分類上可以分為:用戶線程(以上講的都是用戶線程),另一個是守護線程。
    • 其它所有的用戶線程結束,則守護線程退出!因為 daemon守護線程是守護非守護線程的
    • setDaemon(true) 使線程變成守護線程
    • 守護線程一般都是無限執行的.
    • 例如 java 中著名的垃圾回收器GC線程就是一個守護線程。
線程安全和鎖
  • 任何對象都可以是鎖,因為鎖的本質是參照物,類似於紅綠燈,上課鈴等等,關鍵是大家要看一個參照物。
  • 同步代碼塊(以object為鎖)
        synchronized(object){
            [並發邏輯]
        }
  • 加鎖裏的邏輯要盡量少
  • 同步方法
    public synchronized int getTicket(){
        
    }
  • 同步方法(非靜態)是以當前對象作為鎖。
    • 如果用非靜態同步方法加鎖,一般獨立出一個對象池,將同步方法放在對象池內,然後多線程對象每次從同一個對象池取資源。
  • 同步方法(靜態) 是以類作為鎖。
線程生命周期

技術分享圖片
技術分享圖片

  • 技術分享圖片
  • yield();方法可以讓線程方法暫時放棄cpu的搶占,但是一旦放棄後又馬上開始搶,有謙讓的意義
  • Thread.sleep(time) 靜態方法 讓當前線程休眠time毫秒,不去搶占cpu
  • join等到指定的線程執行完後執行

    sleep() 與 wait()的比較
  • 相同點:wait()的作用是讓當前線程由“運行狀態”進入“等待(阻塞)狀態”的同時,也會釋放同步鎖。而sleep()的作用是也是讓當前線程由“運行狀態”進入到“休眠(阻塞)狀態”
  • 不同點:wait()會釋放對象的同步鎖,而sleep()則不會釋放鎖

    join()方法作用
  • 當在主線程當中執行到t1.join()方法時,就認為主線程應該把執行權讓給t1

    線程死亡
  • 有兩個原因會導致線程死亡:
    - 1) run方法正常退出而自然死亡,
    - 2) 一個未捕獲的異常終止了run方法而使線程猝死。
    - 為了確定線程在當前是否存活著(就是要麽是可運行的,要麽是被阻塞了),需要使用isAlive方法。如果是可運行或被阻塞,這個方法返回true; 如果線程仍舊是new狀態且不是可運行的, 或者線程死亡了,則返回false.

阻塞狀態
  • 線程運行過程中,可能由於各種原因進入阻塞狀態:
    • 1>線程通過調用sleep方法進入睡眠狀態;
    • 2>線程調用一個在I/O上被阻塞的操作,即該操作在輸入輸出操作完成之前不會返回到它的調用者;
    • 3>線程試圖得到一個鎖,而該鎖正被其他線程持有;
    • 4>線程在等待某個觸發條件;

      參考
  • https://www.cnblogs.com/skywang12345/p/java_threads_category.html

java線程基礎梳理