day31-執行緒基礎01

執行緒基礎01

1.程式 進程 執行緒

  • 程式(program):是為完成的特定任務,用某種語言編寫的一組指令的集合。簡單來說,就是我們寫的程式碼。

image-20220903181710298

  • 進程:

    1. 進程是指運行中的程式,比如我們使用QQ,就啟動了一個進程,作業系統就會為該進程分配空間。當我們使用迅雷,又啟動了一個進程,作業系統將為迅雷分配新的記憶體空間。
    2. 進程是程式的一次執行過程,或是正在運行的一個程式。是動態過程:有它自身的產生、存在和消亡的過程。

    image-20220903182817371

  • 執行緒:

    1. 執行緒是由進程創建的,是進程的一個實體
    2. 一個進程可以有多個執行緒,比如:用迅雷同時下載多個文件
  • 其他相關概念:

    1. 單執行緒:同一時刻,只允許執行一個執行緒
    2. 多執行緒:同一時刻,可以執行多個執行緒,比如:一個qq進程,可以同時打開多個聊天窗口;一個迅雷進程,可以同時下載多個文件。
    3. 並發:同一時刻,多個任務交替執行,造成一種「貌似同時」的錯覺,簡單地說,單核cpu實現的多任務就是並發

    image-20220903183757457

    1. 並行:同一時刻,多個任務同時執行。多核cpu可以實現並行。在電腦中也可能同時出現並發和並行的狀態。

image-20220903183821311

例子:

package li.thread;

public class CpuNum {
    public static void main(String[] args) {

        Runtime runtime = Runtime.getRuntime();
        //獲取當前的電腦的cpu數量
        int cpuNums = runtime.availableProcessors();
        System.out.println("當前的CPU數量="+cpuNums);//當前的CPU數量=8

    }
}

2.執行緒的基本使用

  • 創建執行緒的兩種方式

在java中執行緒來使用有兩種方法:

  1. 繼承Thread類,重寫run方法
  2. 實現Runnable介面,重寫run方法

image-20220903191317462

2.1繼承Thread創建執行緒

例子1:執行緒應用案例1-繼承Thread類

1)請編寫程式,開啟一個執行緒,該執行緒每隔一秒,在控制台輸出 「喵喵,我是小貓咪」

2)對上題改進:當輸出80次「喵喵,我是小貓咪」時,結束該執行緒

3)使用JConsole監控執行緒執行情況,並畫出程式示意圖

package li.thread;

//演示通過繼承Thread類創建執行緒
public class Thread01 {
    public static void main(String[] args) throws InterruptedException {
        
        //創建一個Cat對象,可以當做執行緒來使用
        Cat cat = new Cat();
        
        cat.start();//啟動執行緒
        
        //當main執行緒啟動一個子執行緒 Thread-0後,主執行緒不會阻塞,會繼續執行
        //這時 主執行緒和子執行緒是交替執行
        System.out.println("主執行緒繼續執行="+Thread.currentThread().getName());//主執行緒繼續執行=main
        for (int i = 0; i < 60; i++) {
            System.out.println("主執行緒 i="+i);
            //讓主執行緒休眠
            Thread.sleep(1000);
        }
    }
}

//1.當一個類繼承了Thread類,該類就可以當做一個執行緒使用
//2.我們會重寫run方法,寫上自己的業務程式碼
//3.run Thread類實現了Runnable介面的run方法
/*
      @Override
      public void run() {
          if (target != null) {
              target.run();
          }
      }
 */
class Cat extends Thread {
    @Override
    public void run() {//重寫run方法,寫上自己的業務邏輯
        int times = 0;
        while (true) {
            //該執行緒每隔1秒,在控制台輸出 「喵喵,我是小貓咪」
            System.out.println("喵喵,我是小貓咪" + (++times)+" 執行緒名稱="+Thread.currentThread().getName());
            //讓該執行緒休眠一秒
            try {
                Thread.sleep(1000);//單位為毫秒 try-catch快捷鍵:Ctrl+Alt+T
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (times == 80) {
                break;//當times到80,退出while,這是執行緒也就退出了
            }
        }
    }
}

3)使用JConsole監控執行緒執行情況,並畫出程式示意圖:

如下,在控制台點擊run,運行程式,在程式運行時,點擊Termial

image-20220903201816455

在控制台輸入JConsole,回車。

image-20220903201903908

點擊本地進程,點擊Thread01,點擊下方連接按鈕:

image-20220903202032834

在彈出窗口中點擊不安全的連接按鈕:

image-20220903202109982

在窗口中點擊「執行緒」:

image-20220903202616380

可以在左下角的執行緒小窗口中看到main執行緒和Thread-0執行緒在同時進行

image-20220903202808116

等待一段時間,可以看到當run窗口的主執行緒 i = 60之後,main執行緒結束

結束前:

image-20220903203153344

結束後:image-20220903203145376

當執行緒名稱=Thread-0輸出到80次時,雖然可以Thread-0還在左下角,但是實際上Thread-0執行緒已經結束了,整個進程隨之結束。

image-20220903204614562

程式示意圖:

202209032116

注意:在多執行緒編程裡面,並不一定說主執行緒結束了,整個進行就結束了,等所有執行緒都結束了,進程才會結束。

2.2為什麼是start?

在2.1的例子中,主方法中定義了cat對象,該對象調用了start方法,start方法會去啟動一個執行緒,最終會執行Cat 類的run方法。

思考一個問題:既然最終都是要調用run方法,為什麼cat對象還要通過start方法對調用run呢?為什麼不直接調用?

答案: 首先通過 對象.run() 方法 可以執行方法,但是不是使用的多執行緒的方式,就是一個普通的方法,沒有真正地啟動一個執行緒。即這時候把run方法執行完畢,才能執行主方法剩下的語句。

如下圖:將cat.start();改為cat.run();之後的運行結果:

在run方法執行完之後才執行主方法剩下的語句

image-20220903213334214


那麼在調用start方法時,整個過程到底是什麼樣子的?

點擊start()方法:可以在start方法中看到一個start0()方法:

image-20220903213931518

點擊start0( )方法:可以看到start0是一個本地方法,由 JVM調用,底層是c/c++實現。

image-20220903214152774

再看看run()方法的源碼:可以看到run方法只是簡單的調用了實現類的run,沒有進行任何的多執行緒處理。

image-20220903215311977

換而言之,Java中真正實現多執行緒的效果的是start0方法,而不是run方法

202209032154

Tags: