日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Java 線程基礎(chǔ)

 Coder編程 2021-05-01

本文部分摘自《Java 并發(fā)編程的藝術(shù)》


線程簡介

1. 什么是線程?

現(xiàn)代操作系統(tǒng)在運(yùn)行一個(gè)程序時(shí),會(huì)為其創(chuàng)建一個(gè)進(jìn)程,一個(gè)進(jìn)程里可以創(chuàng)建多個(gè)線程?,F(xiàn)代操作系統(tǒng)調(diào)度的最小單元是線程,也叫輕量級(jí)進(jìn)程。這些線程都擁有各自的計(jì)數(shù)器、堆棧和局部變量等屬性,并且能訪問共享的內(nèi)存變量。處理器在這些線程上高速切換,讓使用者覺得這些線程在同時(shí)執(zhí)行

2. 為什么使用多線程?

使用多線程的原因主要有以下幾點(diǎn):

  • 更多的處理器核心

    通過使用多線程技術(shù),將計(jì)算邏輯分配到多個(gè)處理器核心上,可以顯著減少程序的處理時(shí)間

  • 更快的響應(yīng)時(shí)間

    有時(shí)我們會(huì)編寫一些較為復(fù)雜的代碼(主要指業(yè)務(wù)邏輯),可以使用多線程技術(shù),將數(shù)據(jù)一致性不強(qiáng)的操作派發(fā)給其他線程處理(也可以使用消息隊(duì)列)。這樣做的好處是響應(yīng)用戶請求的線程能夠盡可能快地處理完成,縮短了響應(yīng)時(shí)間

  • 更好的編程模型

    Java 已經(jīng)為多線程編程提供了一套良好的編程模型,開發(fā)人員只需根據(jù)問題需要建立合適的模型即可


線程優(yōu)先級(jí)

現(xiàn)代操作系統(tǒng)基本采用時(shí)分的形式調(diào)度運(yùn)行的線程,操作系統(tǒng)會(huì)分出一個(gè)個(gè)時(shí)間片,線程分配到若干時(shí)間片,當(dāng)線程的時(shí)間片用完了發(fā)生線程調(diào)度,并等待下次分配。線程分配到的時(shí)間片多少也就決定了線程使用處理器資源的多少,而線程優(yōu)先級(jí)就是決定線程需要多或少分配一些處理器資源的線程屬性

在 Java 線程中,通過一個(gè)整型成員變量 priority 來控制優(yōu)先級(jí),優(yōu)先級(jí)的范圍從 1 ~ 10,在線程構(gòu)建時(shí)可以通過 setPriority(int) 方法來修改優(yōu)先級(jí),默認(rèn)優(yōu)先級(jí)是 5,優(yōu)先級(jí)高的線程分配時(shí)間片的數(shù)量要多于優(yōu)先級(jí)低的線程。不過,在不同的 JVM 以及操作系統(tǒng)上,線程規(guī)劃會(huì)存在差異,有些操作系統(tǒng)甚至?xí)雎跃€程優(yōu)先級(jí)的設(shè)定

public class Priority {

    private static volatile boolean notStart = true;
    private static volatile boolean notEnd = true;

    public static void main(String[] args) throws Exception {
        List<Job> jobs = new ArrayList<Job>();
        for (int i = 0; i < 10; i++) {
            int priority = i < 5 ? Thread.MIN_PRIORITY : Thread.MAX_PRIORITY;
            Job job = new Job(priority);
            jobs.add(job);
            Thread thread = new Thread(job, "Thread:" + i);
            thread.setPriority(priority);
            thread.start();
        }
        notStart = false;
        TimeUnit.SECONDS.sleep(10);
        notEnd = false;
        for (Job job : jobs) {
            System.out.println("Job Priority : " + job.priority + ", Count : " + job.jobCount);
        }
    }

    static class Job implements Runnable {
        private int priority;
        private long jobCount;

        public Job(int priority) {
            this.priority = priority;
        }

        @Override
        public void run() {
            while (notStart) {
                Thread.yield();
            }
            while (notEnd) {
                Thread.yield();
                jobCount++;
            }
        }
    }
}

運(yùn)行該示例,在筆者機(jī)器上對(duì)應(yīng)的輸出如下

筆者使用的環(huán)境為:Win10 + JDK11,從輸出可以看到線程優(yōu)先級(jí)起作用了


線程的狀態(tài)

Java 線程在運(yùn)行的生命周期中可能處于下表所示的六種不同的狀態(tài),在給定的一個(gè)時(shí)刻,線程只能處于其中的一個(gè)狀態(tài)

狀態(tài)名稱 說明
NEW 初始狀態(tài),線程被構(gòu)建,但還沒調(diào)用 start() 方法
RUNNABLE 運(yùn)行狀態(tài),Java 線程將操作系統(tǒng)中的就緒和運(yùn)行兩種狀態(tài)籠統(tǒng)地稱作“運(yùn)行中”
BLOCKED 阻塞狀態(tài),表示線程阻塞于鎖
WAITING 等待狀態(tài),表示線程進(jìn)入等待狀態(tài),進(jìn)入該狀態(tài)表示當(dāng)前線程需要等待其他線程做出一些特定動(dòng)作(通知或中斷)
TIME_WAITING 超時(shí)等待狀態(tài),該狀態(tài)不同于 WAITING,它是可以在指定的時(shí)間自行返回的
TERMINATED 終止?fàn)顟B(tài),表示當(dāng)前線程已經(jīng)執(zhí)行完畢

線程在自身的生命周期中,并不是固定地處于某一狀態(tài),而是隨著代碼的執(zhí)行在不同的狀態(tài)之間進(jìn)行切換


Daemon 線程

Daemon 線程是一種支持型線程,主要被用作程序中后臺(tái)調(diào)度以及支持性工作。這意味著,當(dāng)一個(gè) Java 虛擬機(jī)中不存在 Daemon 線程的時(shí)候,Java 虛擬機(jī)將退出??梢哉{(diào)用 Thread.setDaemon(true) 將線程設(shè)置為 Daemon 線程

使用 Daemon 線程需要注意兩點(diǎn):

  • Daemon 屬性需要在啟動(dòng)線程之前設(shè)置,不能在啟動(dòng)線程之后設(shè)置
  • 在構(gòu)建 Daemon 線程時(shí),不能依靠 finally 塊中的內(nèi)容來確保執(zhí)行或關(guān)閉清理資源的邏輯。因?yàn)樵?Java 虛擬機(jī)退出時(shí) Daemon 線程中的 finally 塊并不一定會(huì)執(zhí)行

啟動(dòng)和終止線程

1. 構(gòu)造線程

在運(yùn)行線程之前首先要構(gòu)造一個(gè)線程對(duì)象,線程對(duì)象在構(gòu)造的時(shí)候需提供線程需的屬性,如線程所屬的線程組、是否是 Daemon 線程等信息

2. 啟動(dòng)線程

線程對(duì)象在初始化完成之后,調(diào)用 start() 方法即可啟動(dòng)線程

3. 理解中斷

中斷可以理解為線程的一個(gè)標(biāo)識(shí)位屬性,標(biāo)識(shí)一個(gè)運(yùn)行中的線程是否被其他線程進(jìn)行了中斷操作。中斷好比其他線程對(duì)該線程打了個(gè)招呼,其他線程可以通過調(diào)用該線程的 interrupt() 方法對(duì)其進(jìn)行中斷操作

線程通過檢查自身是否被中斷進(jìn)行響應(yīng),線程通過 isInterrupted() 來進(jìn)行判斷是否被中斷,也可以調(diào)用靜態(tài)方法 Tread.interrupted() 對(duì)當(dāng)前線程的中斷標(biāo)識(shí)位進(jìn)行復(fù)位。如果線程已經(jīng)處于終結(jié)狀態(tài),即時(shí)線程被中斷過,在調(diào)用該對(duì)象的 isInterrupted() 時(shí)依舊會(huì)返回 false

許多聲明拋出 InterruptedException 的方法在拋出異常之前,Java 虛擬機(jī)會(huì)先將該線程的中斷標(biāo)識(shí)位清除,然后拋出 InterruptedException,此時(shí)調(diào)用 isInterrupted() 方法將會(huì)返回 false

在下面的例子中,首先創(chuàng)建兩個(gè)線程 SleepThread 和 BusyThread,前者不停地睡眠,后者一直運(yùn)行,分別對(duì)兩個(gè)線程分別進(jìn)行中斷操作,觀察中斷標(biāo)識(shí)位

public class Interrupted {

    public static void main(String[] args) throws InterruptedException {
        // sleepThread 不停的嘗試睡眠
        Thread sleepThread = new Thread(new SleepRunner(), "SleepThread");
        sleepThread.setDaemon(true);
        // busyThread 不停的運(yùn)行
        Thread busyThread = new Thread(new BusyRunner(), "BusyThread");
        busyThread.setDaemon(true);
        sleepThread.start();
        busyThread.start();
        // 休眠 5 秒,讓 sleepThread 和 busyThread 充分運(yùn)行
        TimeUnit.SECONDS.sleep(5);
        sleepThread.interrupt();
        busyThread.interrupt();
        System.out.println("SleepThread interrupted is " + sleepThread.isInterrupted());
        System.out.println("BusyThread interrupted is " + busyThread.isInterrupted());
        // 防止 sleepThread 和 busyThreaad 立刻退出
        SleepUtils.second(2);
    }

    static class SleepRunner implements Runnable {

        @Override
        public void run() {
            while (true) {
                SleepUtils.second(10);
            }
        }
    }

    static class BusyRunner implements Runnable {

        @Override
        public void run() {
            while (true) {

            }
        }
    }
}

輸出如下

從結(jié)果可以看出,拋出 InterruptedException 的線程 SleepThread,其中斷標(biāo)識(shí)位被清除了,而一直忙碌運(yùn)行的線程 BusyThread 的中斷標(biāo)識(shí)位沒有被清除

4. 安全地終止線程

前面提到的中斷操作是一種簡便的線程間交互方式,適合用來取消或停止任務(wù)。除了中斷以外,還可以利用一個(gè) boolean 變量來控制是否需要停止任務(wù)并終止線程

下面的示例中,創(chuàng)建了一個(gè)線程 CountThread,它不斷地進(jìn)行變量累加,而主線程嘗試對(duì)其進(jìn)行中斷操作和停止操作

public class Shutdown {

    public static void main(String[] args) throws InterruptedException {
        Runner one = new Runner();
        Thread countThread = new Thread(one, "CountThread");
        countThread.start();
        // 睡眠一秒,main 線程對(duì) CountThread 進(jìn)行中斷,使 CountThread 能夠感知中斷而結(jié)束
        TimeUnit.SECONDS.sleep(1);
        countThread.interrupt();
        Runner two = new Runner();
        countThread = new Thread(two, "CountThread");
        countThread.start();
        // 睡眠一秒,main 線程對(duì) Runner two 進(jìn)行中斷,使 CountThread 能夠感知 on 為 false 而結(jié)束
        TimeUnit.SECONDS.sleep(1);
        two.cancel();
    }

    private static class Runner implements Runnable {

        private long i;
        private volatile boolean on = true;

        @Override
        public void run() {
            while (on && !Thread.currentThread().isInterrupted()) {
                i++;
            }
            System.out.println("Count i = " + i);
        }

        public void cancel() {
            on = false;
        }
    }
}

main 線程通過中斷操作和 cancel() 方法均可使 CountThread 得以終止。這種通過標(biāo)識(shí)位或者中斷操作的方式能夠使線程在終止時(shí)有機(jī)會(huì)去清理資源,而不是武斷地將線程停止,更加安全和優(yōu)雅


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請遵守用戶 評(píng)論公約

    類似文章 更多