多線程1,線程基礎知識

多線程基礎知識

目錄介紹

  • 1.進程概述及多進程的意義[理解]php

    • 1.1 線程和進程
    • 1.2 進程概述
    • 1.3 多進程的意義
  • 2.線程的概述和多線程的意義[理解]java

    • 2.1 什麼是線程
    • 2.2 多線程有什麼意義
    • 2.3 並行和併發
  • 3.JVM運行原理以及JVM啓動的線程探討[理解]安全

    • 3.1 Java程序運行原理
    • 3.2 JVM的啓動是多線程的嗎
  • 4.多線程程序實現的方式[掌握]網絡

    • 4.1 多線程程序實現的方式【重點】
    • 4.1.1 第一種方式:是類繼承Thread
    • 4.1.2 第二種方式:是實現接口Runnable
    • 4.2 多線程兩種方式的區別
    • 4.3 幾個小問題探索
    • 4.4 匿名內部類的方式實現多線程程序
  • 5.線程調度多線程

    • 5.1 線程的調度問題
    • 5.2 線程有兩種調度模型
  • 6.線程控制併發

    • 6.1 線程控制之休眠線程
    • 6.2 線程控制之加入線程
    • 6.3 線程控制之禮讓線程
    • 6.4 線程控制之守護線程
    • 6.5 線程控制之中斷線程
  • 7.案例分析jvm

    • 7.1 繼承Thread類的方式賣電影票案例
    • 7.2 實現Runnable接口的方式賣電影票
    • 7.3 買電影票出現了同票和負數票的緣由分析
    • 7.4 線程安全問題的產生緣由分析
    • 7.5 同步代碼塊的方式解決線程安全問題

0.前沿介紹

1.進程概述及多進程的意義[理解]

1.1 線程和進程

  • 要想說線程,首先必須得聊聊進程,由於線程是依賴於進程存在的。

1.2 進程概述

  • 什麼是進程呢?經過任務管理器咱們就能夠看到進程的存在。
  • 概念:進程就是正在運行的程序,是系統進行資源分配和調用的獨立單位。每個進程都有它本身的內存空間和系統資源。

1.3 多進程的意義

  • 單進程計算機只能作一件事情。而咱們如今的計算機均可以一邊玩遊戲(遊戲進程),一邊聽音樂(音樂進程),因此咱們常見的操做系統都是多進程操做系統。好比:Windows,Mac和Linux等,能在同一個時間段內執行多個任務。
  • 對於單核計算機來說,遊戲進程和音樂進程是同時運行的嗎?不是。
  • 由於CPU在某個時間點上只能作一件事情,計算機是在遊戲進程和音樂進程間作着頻繁切換,且切換速度很快,
  • 因此,咱們感受遊戲和音樂在同時進行,其實並非同時執行的。多進程的做用不是提升執行速度,而是提升CPU的使用率。

2.線程的概述和多線程的意義[理解]

2.1 什麼是線程

  • 在一個進程內部又能夠執行多個任務,而這每個任務咱們就能夠當作是一個線程。是程序使用CPU的基本單位。

2.2 多線程有什麼意義

  • 多線程的做用不是提升執行速度,而是爲了提升應用程序的使用率。
  • 那麼怎麼理解這個問題呢?咱們程序在運行的使用,都是在搶CPU的時間片(執行權),若是是多線程的程序,那麼在搶到CPU的執行權的機率應該比較單線程程序搶到的機率要大.那麼也就是說,CPU在多線程程序中執行的時間要比單線程多,因此就提升了程序的使用率.可是即便是多線程程序,那麼他們中的哪一個線程能搶佔到CPU的資源呢,這個是不肯定的,因此多線程具備隨機性.

2.3 並行和併發

  • 前者是邏輯上同時發生,指在某一個時間內同時運行多個程序。
  • 後者是物理上同時發生,指在某一個時間點同時運行多個程序。

3.JVM運行原理以及JVM啓動的線程探討[理解]

3.1 Java程序運行原理

  • Java命令會啓動java虛擬機,啓動JVM,等於啓動了一個應用程序,也就是啓動了一個進程。
  • 該進程會自動啓動一個 「主線程」 ,而後主線程去調用某個類的 main 方法。因此 main方法運行在主線程中。

3.2 JVM的啓動是多線程的嗎

  • JVM啓動至少啓動了垃圾回收線程和主線程,因此是多線程的。

4.多線程程序實現的方式[掌握]

4.1 多線程程序實現的方式

  • 4.1.1 第一種方式:是類繼承Thread
第一種方式的步驟:
1: 定義一個類,讓該類去繼承Thread類
2: 重寫run方法
3: 建立該類的對象
4: 啓動線程

public class ThreadDemo {
    public static void main(String[] args) {
        // 建立對象
        MyThread t1 = new MyThread() ;
        MyThread t2 = new MyThread() ;
        // 啓動線程: 須要使用start方法啓動線程, 若是咱們在這裏調用的是run方法,那麼咱們只是把該方法做爲普通方法進行執行
//        t1.run() ;
//        t1.run() ;
        t1.start() ;        // 告訴jvm開啓一個線程調用run方法
        // t1.start() ;        // 一個線程只能被啓動一次
        t2.start() ;
        
    }
}

public class MyThread extends Thread {
    @Override
    public void run() {
        for(int x = 0 ; x < 1000 ; x++) {
            System.out.println(x);
        }
    }
}
  • 4.1.2 第二種方式:是實現接口Runnable
實現多線程的第二中方式步驟:
1: 定義一個類,讓該類去實現Runnable接口
2: 重寫run方法
3: 建立定義的類的對象
4: 建立Thread的對象吧第三步建立的對象做爲參數傳遞進來
5: 啓動線程

public static void main(String[] args) {
    // 建立定義的類的對象
    MyThread mt = new MyThread() ;
    // 建立Thread的對象吧第三步建立的對象做爲參數傳遞進來
    Thread t1 = new Thread(mt , "張三") ;
    Thread t2 = new Thread(mt , "李四") ;
    // 啓動線程
    t1.start() ;
    t2.start() ;
}

public class MyThread implements Runnable {
    @Override
    public void run() {
        for(int x = 0 ; x < 1000 ; x++) {
            System.out.println(Thread.currentThread().getName() + "---" + x);
        }
        
    }
}

4.2 多線程兩種方式的區別

  • run()方法只是調用了Thread實例的run()方法而已,它仍然運行在主線程上,而start()方法會開闢一個新的線程,在新的線程上調用run()方法,此時它運行在新的線程上。

4.3 幾個小問題探索

  • 4.3.1 爲何要重寫run方法
  • 能夠在定義的類中,定義多個方法,而方法中的代碼並非全部的都須要線程來進行執行; 若是咱們想讓某一個段代碼被線程,那麼咱們只須要將那一段代碼放在run方法中。那麼也就是說run方法中封裝的都是要被線程執行的代碼 ;
  • run方法中的代碼的特色: 封裝的都是一些比較耗時的代碼
  • 4.3.2 線程能不能屢次啓動
  • 一個線程只能被啓動一次
  • 4.3.2 run()和start()方法的區別
  • 啓動線程: 須要使用start方法啓動線程, 若是咱們在這裏調用的是run方法,那麼咱們只是把該方法做爲普通方法進行執行。

4.4 匿名內部類的方式實現多線程程序

  • new Thread(){代碼…}.start();
  • new Thread(new Runnable(){代碼…}).start();

5.線程調度

5.1 線程的調度問題

  • 應用程序在執行的時候都須要依賴於線程去搶佔CPU的時間片 , 誰搶佔到了CPU的時間片,那麼CPU就會執行誰
  • 線程的執行:假如咱們的計算機只有一個 CPU,那麼 CPU在某一個時刻只能執行一條指令,線程只有獲得CPU時間片,也就是使用權,才能夠執行指令。

5.2 線程有兩種調度模型

  • 5.2.1 分時調度模型
  • 全部線程輪流使用CPU的使用權,平均分配每一個線程佔用 CPU 的時間片
  • 5.2.2 搶佔式調度模型
  • 優先讓優先級高的線程使用 CPU,若是線程的優先級相同,那麼會隨機選擇一個,優先級高的線程獲取的 CPU 時間片相對多一些。Java使用的是搶佔式調度模型。

6.線程控制

6.1 線程控制之休眠線程

  • public static void sleep(long time) ;
  • time表達的意思是休眠的時間 , 單位是毫秒

6.2 線程控制之加入線程

  • public final void join()
  • 等待該線程執行完畢了之後,其餘線程才能再次執行
  • 注意事項: 在線程啓動以後,在調用方法

6.3 線程控制之禮讓線程

  • public static void yield():
  • 暫停當前正在執行的線程對象,並執行其餘線程。
  • 線程禮讓的原理是: 暫定當前的線程,然CPU去執行其餘的線程, 這個暫定的時間是至關短暫的; 當我某一個線程暫定完畢之後,其餘的線程尚未搶佔到cpu的執行權 ; 那麼這個是時候當前的線程會和其餘的線程再次搶佔cpu的執行權;

6.4 線程控制之守護線程

  • public final void setDaemon(boolean on)
  • 將該線程標記爲守護線程或用戶線程。當正在運行的線程都是守護線程時,Java 虛擬機退出。 該方法必須在啓動線程前調用。
  • jvm會線程程序中存在的線程類型,若是線程所有是守護線程,那麼jvm就中止。

6.5 線程控制之中斷線程

  • public final void stop():
  • 中止線程的運行
  • public void interrupt():
  • 中斷線程(這個翻譯不太好),查看API可得當線程調用wait(),sleep(long time)方法的時候處於阻塞狀態,能夠經過這個方法清除阻塞

7.案例分析

7.1 繼承Thread類的方式賣電影票案例

public class ThreadDemo {
    public static void main(String[] args) {
        /**
         * 需求:某電影院目前正在上映賀歲大片,共有100張票,而它有3個售票窗口售票,請設計一個程序模擬該電影院售票。
         */
        // 建立3個線程對象
        SellTicktes t1 = new SellTicktes() ;
        SellTicktes t2 = new SellTicktes() ;
        SellTicktes t3 = new SellTicktes() ;
        // 設置名稱
        t1.setName("窗口1") ;
        t2.setName("窗口2") ;
        t3.setName("窗口3") ;
        // 啓動線程
        t1.start() ;
        t2.start() ;
        t3.start() ;
    }
}

public class SellTicktes extends Thread {
    private static int num = 100 ;
    @Override
    public void run() {
        /**
         * 定義總票數
         * 
         * 若是咱們把票數定義成了局部變量,那麼表示的意思是每個窗口出售了各自的100張票; 而咱們的需求是: 總共有100張票
         * 而這100張票要被3個窗口出售; 所以咱們就不能把票數定義成局部變量,只能定義成成員變量
         */        
        // 模擬售票
        while(true) {
            if( num > 0 ) {
                System.out.println(Thread.currentThread().getName() + "正在出售" + (num--) + "張票");
            }
        }
    }
}

7.2 實現Runnable接口的方式賣電影票

public class SellTicektesDemo {
    public static void main(String[] args) {
        // 建立SellTicektes對象
        SellTicektes st = new SellTicektes() ;
        // 建立Thread對象
        Thread t1 = new Thread(st , "窗口1") ;
        Thread t2 = new Thread(st , "窗口2") ;
        Thread t3 = new Thread(st , "窗口3") ;
        // 啓動線程
        t1.start() ;
        t2.start() ;
        t3.start() ;
    }
}

public class SellTicektes implements Runnable {
    private static int num = 100 ;
    @Override
    public void run() {
        while(true) {
            if(num > 0) {
                System.out.println(Thread.currentThread().getName() + "正在出售第" + (num--) + "張票");
            }
        }
    }
}

7.3 買電影票出現了同票和負數票的緣由分析

  • 講解過電影院售票程序,從表面上看不出什麼問題,可是在真實生活中,售票時網絡是不能實時傳輸的,老是存在延遲的狀況,因此,在出售一張票之後,須要一點時間的延遲。改實現接口方式的賣票程序,每次賣票延遲100毫秒
public class ThreadDemo {
    public static void main(String[] args) {
        // 建立3個線程對象
        SellTicktes t1 = new SellTicktes() ;
        SellTicktes t2 = new SellTicktes() ;
        SellTicktes t3 = new SellTicktes() ;
        // 設置名稱
        t1.setName("窗口1") ;
        t2.setName("窗口2") ;
        t3.setName("窗口3") ;
        // 啓動線程
        t1.start() ;
        t2.start() ;
        t3.start() ;
    }
}

public class SellTicktes extends Thread {
    private static int num = 100 ;
    @Override
    public void run() {
        // 模擬售票
        while(true) {
            if( num > 0 ) {
                try {
                    Thread.sleep(100) ;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "正在出售" + (num--) + "張票");
            }
        }
    }
}

7.4 線程安全問題的產生緣由分析

  • 7.4.1 首先想爲何出現問題?
  • 是不是多線程環境,是否有共享數據,是否有多條語句操做共享數據
  • 7.4.2 如何解決多線程安全問題呢?
  • 基本思想:讓程序沒有安全問題的環境。怎麼實現呢?把多個語句操做共享數據的代碼給鎖起來,讓任意時刻只能有一個線程執行便可。

7.5 同步代碼塊的方式解決線程安全問題

  • 7.5.1 同步代碼塊的格式
  • 同步能夠解決安全問題的根本緣由就在那個對象上。該對象如同鎖的功能
synchronized(對象){
            須要同步的代碼;
        }
  • 7.5.2 同步代碼塊優點和劣勢
  • 同步的好處:同步的出現解決了多線程的安全問題。
  • 同步的弊端:當線程至關多時,由於每一個線程都會去判斷同步上的鎖,這是很耗費資源的,無形中會下降程序的運行效率。
相關文章
相關標籤/搜索