第一章:初識線程

1.1 線程的介紹

對計(jì)算機(jī)來說每一個任務(wù)就是一個進(jìn)程(process),在每一個進(jìn)程內(nèi)部至少要有一個線程(thread)實(shí)在運(yùn)行中,有時線程也稱為輕量級的進(jìn)程。
線程是程序執(zhí)行的一個路徑,每一個線程都有自己的局部變量表、程序計(jì)數(shù)器(指向正在執(zhí)行的指令指針)以及各自的生命周期。當(dāng)啟動了一個Java虛擬機(jī)(jvm)時,從操作系統(tǒng)開始就會創(chuàng)建一個新的進(jìn)程(jvm進(jìn)程),jvm進(jìn)程中將會派生或者創(chuàng)建很多線程。

1.2 線程的生命周期大體可以分為如下5個主要的階段

  • NEW(創(chuàng)建)
  • RUNNABLE(可運(yùn)行的)
  • RUNNING(運(yùn)行中)
  • BLOCKED(阻塞)
  • TERMINATED(結(jié)束)

1.2.1 線程的new狀態(tài)

當(dāng)我們用關(guān)鍵字new創(chuàng)建了一個Thread對象時,此時它并不處于可執(zhí)行狀態(tài),因?yàn)椴]有調(diào)用start方法啟動該線程。此時的狀態(tài)準(zhǔn)確的說,它只是Thread對象的狀態(tài),在沒有start之前,該線程根本不存在,跟用new創(chuàng)建一個普通的Java對象沒啥區(qū)別
new狀態(tài)通過start方法進(jìn)入runnable狀態(tài)

1.2.2 線程的runnable狀態(tài)

線程對象進(jìn)入runnable狀態(tài)必須得調(diào)用start方法。此時才真正得在jvm進(jìn)程中創(chuàng)建了一個線程。線程一經(jīng)啟動不一定就立即得到啟動,線程得運(yùn)行和進(jìn)程一樣都得聽CPU的調(diào)度。此時這個中間狀態(tài),我們稱之為runnable(可執(zhí)行狀態(tài)),具備了執(zhí)行的資格。
由于存在running狀態(tài),所以不會直接進(jìn)入blocked狀態(tài)和terminated狀態(tài),即使是在線程的執(zhí)行邏輯中調(diào)用wait,sleep或者其它的block的io操作等,也必須先獲得CPU的調(diào)度執(zhí)行權(quán)才可以。runnable的線程只能意外終止或進(jìn)入running狀態(tài)

1.2.3 線程的running狀態(tài)

一旦CPU通過輪詢或者其它方式從任務(wù)可執(zhí)行隊(duì)列中選中了線程,此時該線程才能真正的執(zhí)行執(zhí)行的邏輯代碼。一個正處于running的線程也可以被稱之為runnable狀態(tài),反之不行
該狀態(tài)中,線程的狀態(tài)可以發(fā)生如下的狀態(tài)轉(zhuǎn)換

  • 直接進(jìn)入terminated(結(jié)束)狀態(tài),比如調(diào)用jdk已經(jīng)不推薦使用的stop方法或者判斷某個邏輯標(biāo)識。
  • 進(jìn)入blocked(阻塞)狀態(tài),比如調(diào)用了sleep,或者wait方法而加入了waitset中。
  • 進(jìn)行某個阻塞的io操作,比如因網(wǎng)絡(luò)數(shù)據(jù)的讀寫而進(jìn)入了blocked狀態(tài)。
  • 獲取某個鎖資源,從而加入到該鎖的阻塞隊(duì)列中。
  • 由于CPU的調(diào)度器輪詢使該線程放棄執(zhí)行,進(jìn)入runnable狀態(tài)。
  • 線程主動調(diào)用yield(讓步)方法,放棄CPU執(zhí)行權(quán),進(jìn)入runnable狀態(tài)。

1.2.4 線程的blocked狀態(tài)

線程的blocked的狀態(tài)就是被阻塞的狀態(tài)。那么已經(jīng)是blocked的線程可以轉(zhuǎn)變?yōu)槠渌裁礌顟B(tài)了?

  • 直接進(jìn)入terminated(結(jié)束)狀態(tài),比如調(diào)用jdk已經(jīng)不推薦使用的stop方法或者意外死亡(jvm crash虛擬機(jī)崩潰了)。
  • 線程阻塞的操作結(jié)束,比如讀取了想要的數(shù)據(jù)字節(jié)進(jìn)入到runnable狀態(tài)。
  • 線程外城了指定時間的休眠,進(jìn)入到了runnable狀態(tài)。
  • wait中的線程被其它線程notify/notifyall(通知/全部通知)喚醒,進(jìn)入runnable狀態(tài)。
  • 線程獲取到了某個鎖資源,進(jìn)入runnable狀態(tài)。
  • 線程在阻塞過程中被打斷,比如其他線程調(diào)用了interrupt(中斷)方法,進(jìn)入runnable

1.2.5 線程的terminated狀態(tài)

terminated是一個線程的最終狀態(tài),在該狀態(tài)中線程將不會切換到其它任何狀態(tài)。線程進(jìn)入terminated狀態(tài),就表示著該線程的整個生命周期都結(jié)束了。會進(jìn)入到terminated狀態(tài)的情況有如下:

  • 線程運(yùn)行正常結(jié)束、結(jié)束生命周期。
  • 線程意外出錯結(jié)束
  • jvm crash,導(dǎo)致所有的線程都結(jié)束。


    線程的生命周期.png

創(chuàng)建線程只有一種方式,那就是構(gòu)造thread類。而實(shí)現(xiàn)線程的執(zhí)行單元則有兩種方式,第一種是重寫thread的run方法,第二種是實(shí)現(xiàn)runnable接口的run方法,并且將runnable實(shí)例用作構(gòu)造thread的參數(shù)

  • 構(gòu)造thread類重寫run方式是一種模板設(shè)計(jì)模式
package com.jinzheyi;

/**
 * 繼承thread的模板設(shè)計(jì)模式
 */
public class TicketWindow extends Thread {

    private final String name;

    private static final int MAX = 50;

    private static int index = 1;

    public TicketWindow(String name){
        this.name = name;
    }

    @Override
    public void run() {
        while (index<=MAX){
            System.out.println("柜臺:"+name + "當(dāng)前的號碼是:"+ index++);
        }
    }

    public static void main(String[] args) {
        new TicketWindow("一號柜機(jī)").start();
        new TicketWindow("二號柜機(jī)").start();
        new TicketWindow("三號柜機(jī)").start();
        new TicketWindow("四號柜機(jī)").start();
    }
}

打印結(jié)果

D:\soft\Java\jdk1.8.0_152\bin\java.exe "-javaagent:D:\soft\JetBrains\IntelliJ IDEA 2019.3.2\lib\idea_rt.jar=55224:D:\soft\JetBrains\IntelliJ IDEA 2019.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\soft\Java\jdk1.8.0_152\jre\lib\charsets.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\deploy.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\access-bridge-64.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\cldrdata.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\dnsns.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\jaccess.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\jfxrt.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\localedata.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\nashorn.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\sunec.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\sunjce_provider.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\sunmscapi.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\sunpkcs11.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\zipfs.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\javaws.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\jce.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\jfr.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\jfxswt.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\jsse.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\management-agent.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\plugin.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\resources.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\rt.jar;E:\idea-workspace\springboot\thread\thread-1\out\production\thread-1 com.jinzheyi.TicketWindow
柜臺:一號柜機(jī)當(dāng)前的號碼是:1
柜臺:二號柜機(jī)當(dāng)前的號碼是:2
柜臺:二號柜機(jī)當(dāng)前的號碼是:3
柜臺:二號柜機(jī)當(dāng)前的號碼是:4
柜臺:二號柜機(jī)當(dāng)前的號碼是:5
柜臺:二號柜機(jī)當(dāng)前的號碼是:6
柜臺:二號柜機(jī)當(dāng)前的號碼是:7
柜臺:二號柜機(jī)當(dāng)前的號碼是:8
柜臺:三號柜機(jī)當(dāng)前的號碼是:9
柜臺:一號柜機(jī)當(dāng)前的號碼是:11
柜臺:二號柜機(jī)當(dāng)前的號碼是:10
柜臺:二號柜機(jī)當(dāng)前的號碼是:15
柜臺:二號柜機(jī)當(dāng)前的號碼是:16
柜臺:二號柜機(jī)當(dāng)前的號碼是:17
柜臺:二號柜機(jī)當(dāng)前的號碼是:18
柜臺:二號柜機(jī)當(dāng)前的號碼是:19
柜臺:一號柜機(jī)當(dāng)前的號碼是:14
柜臺:四號柜機(jī)當(dāng)前的號碼是:13
柜臺:三號柜機(jī)當(dāng)前的號碼是:12
柜臺:四號柜機(jī)當(dāng)前的號碼是:22
柜臺:一號柜機(jī)當(dāng)前的號碼是:21
柜臺:二號柜機(jī)當(dāng)前的號碼是:20
柜臺:一號柜機(jī)當(dāng)前的號碼是:25
柜臺:四號柜機(jī)當(dāng)前的號碼是:24
柜臺:三號柜機(jī)當(dāng)前的號碼是:23
柜臺:四號柜機(jī)當(dāng)前的號碼是:28
柜臺:一號柜機(jī)當(dāng)前的號碼是:27
柜臺:二號柜機(jī)當(dāng)前的號碼是:26
柜臺:一號柜機(jī)當(dāng)前的號碼是:31
柜臺:四號柜機(jī)當(dāng)前的號碼是:30
柜臺:三號柜機(jī)當(dāng)前的號碼是:29
柜臺:四號柜機(jī)當(dāng)前的號碼是:34
柜臺:一號柜機(jī)當(dāng)前的號碼是:33
柜臺:二號柜機(jī)當(dāng)前的號碼是:32
柜臺:一號柜機(jī)當(dāng)前的號碼是:37
柜臺:四號柜機(jī)當(dāng)前的號碼是:36
柜臺:三號柜機(jī)當(dāng)前的號碼是:35
柜臺:四號柜機(jī)當(dāng)前的號碼是:40
柜臺:一號柜機(jī)當(dāng)前的號碼是:39
柜臺:二號柜機(jī)當(dāng)前的號碼是:38
柜臺:一號柜機(jī)當(dāng)前的號碼是:43
柜臺:四號柜機(jī)當(dāng)前的號碼是:42
柜臺:三號柜機(jī)當(dāng)前的號碼是:41
柜臺:四號柜機(jī)當(dāng)前的號碼是:46
柜臺:一號柜機(jī)當(dāng)前的號碼是:45
柜臺:二號柜機(jī)當(dāng)前的號碼是:44
柜臺:一號柜機(jī)當(dāng)前的號碼是:49
柜臺:四號柜機(jī)當(dāng)前的號碼是:48
柜臺:三號柜機(jī)當(dāng)前的號碼是:47
柜臺:二號柜機(jī)當(dāng)前的號碼是:50

Process finished with exit code 0
  • 策略設(shè)計(jì)模式
package com.jinzheyi;

/**
 * @Description 實(shí)現(xiàn)runnable接口
 * @Author jin_z
 * @Date 2021/3/21 17:42
 * @since:
 * @copyright:
 */
public class TicketWindowRunnable implements Runnable {

    private int index = 1;  //不做static修飾

    public final static int MAX = 50;


    @Override
    public void run() {
        while (index<=MAX){
            System.out.println(Thread.currentThread() + "的號碼是:"+ index++);
        }
    }

    public static void main(String[] args) {
        final TicketWindowRunnable ticketWindowRunnable = new TicketWindowRunnable();
        new Thread(ticketWindowRunnable,"一號柜機(jī)").start();
        new Thread(ticketWindowRunnable,"二號柜機(jī)").start();
        new Thread(ticketWindowRunnable,"三號柜機(jī)").start();
        new Thread(ticketWindowRunnable,"四號柜機(jī)").start();
    }

}
D:\soft\Java\jdk1.8.0_152\bin\java.exe "-javaagent:D:\soft\JetBrains\IntelliJ IDEA 2019.3.2\lib\idea_rt.jar=55977:D:\soft\JetBrains\IntelliJ IDEA 2019.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\soft\Java\jdk1.8.0_152\jre\lib\charsets.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\deploy.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\access-bridge-64.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\cldrdata.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\dnsns.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\jaccess.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\jfxrt.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\localedata.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\nashorn.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\sunec.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\sunjce_provider.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\sunmscapi.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\sunpkcs11.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\ext\zipfs.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\javaws.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\jce.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\jfr.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\jfxswt.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\jsse.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\management-agent.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\plugin.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\resources.jar;D:\soft\Java\jdk1.8.0_152\jre\lib\rt.jar;E:\idea-workspace\springboot\thread\thread-1\out\production\thread-1 com.jinzheyi.TicketWindowRunnable
Thread[一號柜機(jī),5,main]的號碼是:1
Thread[四號柜機(jī),5,main]的號碼是:3
Thread[三號柜機(jī),5,main]的號碼是:2
Thread[二號柜機(jī),5,main]的號碼是:1
Thread[三號柜機(jī),5,main]的號碼是:6
Thread[四號柜機(jī),5,main]的號碼是:5
Thread[一號柜機(jī),5,main]的號碼是:4
Thread[四號柜機(jī),5,main]的號碼是:9
Thread[四號柜機(jī),5,main]的號碼是:11
Thread[四號柜機(jī),5,main]的號碼是:12
Thread[四號柜機(jī),5,main]的號碼是:13
Thread[四號柜機(jī),5,main]的號碼是:14
Thread[四號柜機(jī),5,main]的號碼是:15
Thread[四號柜機(jī),5,main]的號碼是:16
Thread[三號柜機(jī),5,main]的號碼是:8
Thread[二號柜機(jī),5,main]的號碼是:7
Thread[三號柜機(jī),5,main]的號碼是:18
Thread[四號柜機(jī),5,main]的號碼是:17
Thread[一號柜機(jī),5,main]的號碼是:10
Thread[四號柜機(jī),5,main]的號碼是:21
Thread[三號柜機(jī),5,main]的號碼是:20
Thread[二號柜機(jī),5,main]的號碼是:19
Thread[三號柜機(jī),5,main]的號碼是:24
Thread[四號柜機(jī),5,main]的號碼是:23
Thread[一號柜機(jī),5,main]的號碼是:22
Thread[四號柜機(jī),5,main]的號碼是:27
Thread[三號柜機(jī),5,main]的號碼是:26
Thread[二號柜機(jī),5,main]的號碼是:25
Thread[三號柜機(jī),5,main]的號碼是:30
Thread[四號柜機(jī),5,main]的號碼是:29
Thread[一號柜機(jī),5,main]的號碼是:28
Thread[四號柜機(jī),5,main]的號碼是:33
Thread[三號柜機(jī),5,main]的號碼是:32
Thread[二號柜機(jī),5,main]的號碼是:31
Thread[三號柜機(jī),5,main]的號碼是:36
Thread[三號柜機(jī),5,main]的號碼是:38
Thread[四號柜機(jī),5,main]的號碼是:35
Thread[一號柜機(jī),5,main]的號碼是:34
Thread[四號柜機(jī),5,main]的號碼是:40
Thread[三號柜機(jī),5,main]的號碼是:39
Thread[二號柜機(jī),5,main]的號碼是:37
Thread[三號柜機(jī),5,main]的號碼是:43
Thread[四號柜機(jī),5,main]的號碼是:42
Thread[一號柜機(jī),5,main]的號碼是:41
Thread[四號柜機(jī),5,main]的號碼是:46
Thread[三號柜機(jī),5,main]的號碼是:45
Thread[二號柜機(jī),5,main]的號碼是:44
Thread[三號柜機(jī),5,main]的號碼是:49
Thread[四號柜機(jī),5,main]的號碼是:48
Thread[一號柜機(jī),5,main]的號碼是:47
Thread[二號柜機(jī),5,main]的號碼是:50

Process finished with exit code 0

注意:不管是模板設(shè)計(jì)模式中繼承thread重寫run方法的實(shí)現(xiàn)方式還是策略設(shè)計(jì)模式實(shí)現(xiàn)runnable接口的實(shí)現(xiàn)方式。這兩種方式的代碼多運(yùn)行幾次或者最大值改大點(diǎn),都會出現(xiàn)某個號碼沒讀到或者某個號碼重讀,甚至超讀(讀取的號碼超出最大值)。這是因?yàn)檫@里的共享資源index存在線程安全的問題。
flag:由于作者還在學(xué)習(xí)階段,這里的bug問題,學(xué)到后面的數(shù)據(jù)同步的時候會解決這里的bug

作者:金哲一(jinzheyi)【筆名】
本文代碼地址:https://gitee.com/jinzheyi/springboot/tree/master/thread/thread-1
本文鏈接:http://www.itdecent.cn/p/333444471b3d

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容