day15 15、認真學習多線程

15、多線程總結

最近很認真的學習完了多線程,現在總結一波。加強自己的理解!(因爲可能有錯可以被指出嘛😱)

15.1 什麼是多線程?

15.1.1 第一步:瞭解進程與線程的區別

我們先來看張圖,按照①②③去讀一下這個流程,我們就能在腦海裏形成一個模糊概念。

在這裏插入圖片描述
下面開始進行文字介紹:
進程是啥呢?

  • 進程是指在系統中正在運行的一個應用程序

  • 每個進程之間是獨立的,每個進程均運行在其專用且受保護的內存空間內

比如同時打開迅雷、Xcode,系統就會分別啓動2個進程。
在這裏插入圖片描述
什麼是線程呢?

  • 就是進程裏邊一個負責程序執行的控制單元(就是某個功能啊,某個任務啊,也可以理解爲執行路徑)
  • 進程的任務都由線程來控制,執行;
  • 一個進程沒有線程也就是沒有任務、沒有執行路徑,所以一個進程至少擁有一個線程!

例子:

在這裏插入圖片描述
線程的串行

  • 1個線程中任務的執行是串行的。順序進行
    假設一個線程任務是煲水煮飯,那它就必須先煲水,煲完水,才能去煮飯
    在這裏插入圖片描述
    這就產生了一個效率問題了,我們現實中肯定是煲水和煮飯一起幹的啦!上圖中,文件也是可以同時開始下載的。這就有了多線程。

15.1.2 多線程概念

概念

  • 1、每條線程擁有自己的運行內容——線程任務
  • 2、當一個進程開啓了多條線程,每條線程可以並行(同時)執行,它們線程執行的任務不同,這就是多線程體現
  • 3、開啓多個線程就能同時執行多個任務
  • 4、多線程技術可以提高程序的執行效率

比如我們實現一個hello的代碼
java虛擬機就會創建很多線程,其中有一個是Main線程,來執行我們的代碼——打印hello world;
其他線程例如GC垃圾回收線程等。當Main線程執行完,我們的GC垃圾回收機制可能還沒執行完。就好像我們360殺毒完畢,清理垃圾還沒行!

並行的實質(多線程的實質)

在這裏插入圖片描述
這也帶來了一個很常見的問題:線程開啓過多會卡
比如我們打開360,把裏面的殺毒,文件清理,垃圾清理,驅動更新全打開
然後又打開酷狗聽歌,下載歌,打開百度網盤下載文件等,開啓了好多的線程,cpu在這些線程裏邊隨機切換,每個線程平均被切換到的概率降低,我們就會覺得卡了,這個時候cpu切換到同一個線程的任務時間變長,自然也就是卡了,會耗費大量cpu資源
在這裏插入圖片描述
這個圖標數字一下飛昇!
在這裏插入圖片描述
這個時候,就出現了傳說中的多核技術——4核、8核等,每個cpu自由調度切換線程。

多線程的好處、弊端

  • 好處:解決了多部分任務同時運行
  • 弊端:正如上面所示,當線程開啓的多了,就會降低cpu的效率。

15.2 線程的創建(兩種方式)

15.2.1 方式一:繼承Thread類

四步實現多線程

  • 創建一個繼承Thread類的子類
  • 覆寫run()方法,實現線程任務的封裝
  • 創建多個線程對象
  • 每個對象都開啓線程——start()方法

代碼演示

package ThreadTest;

/*
目的:練習使用多線程類創建多線程
@Override
    public void run() {//Thread的run方法源碼
        if (target != null) {
            target.run();
        }
    }
 */
 //1、創建繼承Thread的子類
class Animal extends Thread {
    private String name;

    public Animal(String name) {
        super(name);
        this.name = name;
    }
	//2、覆寫run方法
    public void run() {
        // System.out.println(4/0);,其他線程發生異常不影響主線程
        for (int i = 0; i < 10; i++)
            System.out.println("大家好,我叫" + name + "...線程名" + Thread.currentThread().getName());
        //show();
    }

    void show() {
        for (int i = -9999; i < 9999; i++) {
        }
        System.out.print("大家好,我叫" + name + "..." + Thread.currentThread().getName());
    }

}

public class Practice1 {
    public static void main(String[] args) {
		//3、創建線程子類對象
        Animal test2 = new Animal("旺財");//創建對象時就已經安排好線程名字了
        Animal test1 = new Animal("小強");
 		//4、每個線程對象開啓線程!

        test1.start();//線程1
        test2.start();//線程2
        //System.out.println(3/0);//主線程發生異常不影響其他線程,其他線程發送異常也不影響主線程
        for (int i = 0; i < 10; i++)
            System.out.println("haha" + i + "...線程名+" + Thread.currentThread().getName());//主線程,如果去掉循環,其實也是隨機的
        //三個線程隨機運行,搶奪資源!


    }
}

運行結果
在這裏插入圖片描述
思考:直接使用run()和使用start()方法有區別
run方法無法開啓線程,相當於正常的對象調用方法;start()開啓線程運行任務。
線程對象.getName()與Thread.currentThread.getName()
在這裏插入圖片描述
在這裏插入圖片描述
第一是返回的是線程對象默認的一個線程名,第二個返回的是正在運行的線程名,調用的是底層代碼

15.2.2 方式二:實現Runnable接口

Runnable接口是用來封裝線程任務的一個接口,裏面只有一個抽象run方法,用於封裝線程任務。
在這裏插入圖片描述
在這裏插入圖片描述
步驟

  • 創建一個類實現Runnable接口
  • 必須重寫run()方法,否則該類要聲明爲抽象類而且無法實現多線程
  • 創建該類對象,調用Thread的有參構造方法,參數爲Runnable類及其子類對象
  • 開啓線程

代碼演示

package ThreadTest;

public class Practice2 implements Runnable {//1、實現Runnbale接口
    public void show() {
        for (int i = 0; i < 20; i++)
            System.out.println("大家好,我是show!" + i);
    }

    @Override
    public void run() {//2、寫run方法,封裝線程任務
        show();
    }

    public static void main(String[] args) {
    	//創建Runnable子類對象
        Practice2 test = new Practice2();
        //調用Thread有參構造方法
        Thread thread = new Thread(test);
        //開啓線程
        thread.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("現在是主線程執行" + i);//線程之間互相不影響,並且隨機切換。
        }
        thread.stop();//進入線程的凍結時期,隨機凍結
        for (int i = 0; i < 20; i++) {
            System.out.println("現在是主線程執行" + i);//線程之間互相不影響,並且隨機切換。
        }


    }
}

運行結果:
在這裏插入圖片描述

15.2.3 兩種方法的區別及其優劣性

15.2.4 多線程是如何執行的(剖析內存中的情況)

15.3 線程的狀態淺析

自己畫了一張圖:
在這裏插入圖片描述



## 15.4 多線程的使用及其方法介紹
### 
## 15.5 多線程裏邊的問題
### 15.5.1 多次開啓同一個線程會怎麼樣?
## 15.6 多線程中的線程安全問題(同步的引出)
### 15.6.1 挖煤搬礦例子
### 15.6.2 單例設計模式下的線程安全問題
## 15.7 同步裏邊的線程安全問題
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章