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: