自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一文學(xué)會(huì)終止線程的兩種方式

開發(fā) 前端
雖然本文主題是終止線程,但其實(shí)可以推廣到所有while(true)的場(chǎng)景中。在可能出現(xiàn)死循環(huán)或者有條件的大循環(huán)中,我們都應(yīng)該適當(dāng)?shù)脑黾訕?biāo)記位,可以快速終止循環(huán)。

你好,我是看山。

在Java中,終止一個(gè)線程(Kill a Thread)還是有一定技巧的,本文提供兩種方式。

一、使用標(biāo)志位

我們先創(chuàng)建一個(gè)線程類,run方法循環(huán)執(zhí)行一些邏輯,永遠(yuǎn)不會(huì)終止,且不會(huì)自行結(jié)束。為了服務(wù)的穩(wěn)定,我們需要一種方法停止線程。

本節(jié)給出標(biāo)志位法,簡(jiǎn)單說(shuō)就是,有一個(gè)原子標(biāo)志位,可以用來(lái)標(biāo)記當(dāng)前循環(huán)是否執(zhí)行,如果不可執(zhí)行,則跳出循環(huán),當(dāng)前線程即結(jié)束。

public class ControlSubThread extends Thread {
    private final AtomicBoolean running = new AtomicBoolean(false);
    private final AtomicBoolean stopped = new AtomicBoolean(true);
    private final int interval;
    private final AtomicInteger count = new AtomicInteger(0);

    public ControlSubThread(int sleepInterval) {
        interval = sleepInterval;
    }

    @Override
    public void start() {
        Thread worker = new Thread(this);
        worker.start();
    }

    public void shutdown() {
        running.set(false);
        System.out.println("線程正在關(guān)閉,當(dāng)前count為:" + count.get());
    }

    @Override
    public void run() {
        running.set(true);
        stopped.set(false);
        while (running.get()) {
            try {
                System.out.println("線程正在運(yùn)行(" + System.currentTimeMillis() / 1000 + "): " + count.incrementAndGet());
                Thread.sleep(interval);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                System.out.println("線程被中斷,操作未能完成");
            }
            // 在此處執(zhí)行一些操作
        }
        stopped.set(true);
    }
}

我們?cè)趙hile循環(huán)使用了一個(gè)AtomicBoolean,通過控制這個(gè)標(biāo)志位的true或false來(lái)啟動(dòng)或終止循環(huán)。循環(huán)終止了,線程自然也就結(jié)束了。

而且需要注意,考慮到JMM,標(biāo)志位一定要被volatile定義的,為了簡(jiǎn)答,我們這里選擇了AtomicBoolean作為標(biāo)志位。

我們看下應(yīng)用:

final ControlSubThread thread = new ControlSubThread(1000);
thread.start();

System.out.println("主線程等待");
TimeUnit.SECONDS.sleep(10);

thread.shutdown();
thread.join();
System.out.println("主線程終止");

運(yùn)行結(jié)果為:

主線程等待 線程正在運(yùn)行(1733318881): 1 線程正在運(yùn)行(1733318882): 2 線程正在運(yùn)行(1733318883): 3 線程正在運(yùn)行(1733318884): 4 線程正在運(yùn)行(1733318885): 5 線程正在運(yùn)行(1733318886): 6 線程正在運(yùn)行(1733318887): 7 線程正在運(yùn)行(1733318888): 8 線程正在運(yùn)行(1733318889): 9 線程正在運(yùn)行(1733318890): 10 線程正在關(guān)閉,當(dāng)前count為:10 主線程終止

二、中斷線程

上面的實(shí)現(xiàn)方式會(huì)存在一種問題,如果sleep()方法時(shí)間過長(zhǎng),或者運(yùn)行過程出現(xiàn)死鎖,永遠(yuǎn)不會(huì)執(zhí)行到下一次判斷,那將長(zhǎng)時(shí)間阻塞后者無(wú)法清除線程。

這個(gè)時(shí)候我們可以使用interrupt()方法,我們對(duì)上面的示例稍加改造:

public class InterruptSubThread extends Thread {
    private final AtomicBoolean running = new AtomicBoolean(false);
    private final AtomicBoolean stopped = new AtomicBoolean(true);
    private final int interval;
    private final Thread worker;
    private final AtomicInteger count = new AtomicInteger(0);

    public InterruptSubThread(int sleepInterval) {
        interval = sleepInterval;
        worker = new Thread(this);
    }

    @Override
    public void start() {
        worker.start();
    }

    public void shutdown() {
        running.set(false);
    }

    @Override
    public void interrupt() {
        running.set(false);
        worker.interrupt();
    }

    @Override
    public void run() {
        running.set(true);
        stopped.set(false);
        while (running.get()) {
            try {
                System.out.println("線程正在運(yùn)行(" + System.currentTimeMillis() / 1000 + "): " + count.incrementAndGet());
                Thread.sleep(interval);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                System.out.println("線程被中斷,操作未能完成");
            }
            // 在此處執(zhí)行一些操作
        }
        stopped.set(true);
    }
}

在上面示例中,我們添加了一個(gè)interrupt()方法,該方法將我們的running標(biāo)志設(shè)置為false,并調(diào)用工作線程的interrupt()方法。

Thread.interrupt()在使用上時(shí)有一些限制的:

  • 權(quán)限校驗(yàn):如果當(dāng)前線程不是要中斷的線程,Thread.interrupt() 方法會(huì)調(diào)用checkAccess() 方法進(jìn)行權(quán)限檢查。如果沒有足夠的權(quán)限,會(huì)拋出SecurityException;
  • 無(wú)法強(qiáng)制終止線程:Thread.interrupt()方法不能強(qiáng)制終止線程。它只是設(shè)置中斷狀態(tài),線程如何響應(yīng)中斷完全取決于線程自身的實(shí)現(xiàn)。如果線程忽略了中斷狀態(tài),線程將繼續(xù)運(yùn)行。
  • I/O 操作的中斷:對(duì)于阻塞在 I/O 操作上的線程,中斷操作會(huì)關(guān)閉 I/O 通道并拋出ClosedByInterruptException。這可能導(dǎo)致資源泄漏或未完成的操作,因此需要謹(jǐn)慎處理。

如果在調(diào)用此方法時(shí)線程正在睡眠,sleep()方法將拋出InterruptedException異常退出,其他任何阻塞調(diào)用也會(huì)如此。這樣能夠快速打斷休眠和暫停。

在這個(gè)快速教程中,我們研究了如何使用原子變量,并可選擇結(jié)合調(diào)用interrupt()方法,來(lái)干凈地關(guān)閉一個(gè)線程。這絕對(duì)比調(diào)用已棄用的stop()方法要好,因?yàn)檎{(diào)用stop()方法可能會(huì)導(dǎo)致永遠(yuǎn)鎖定和內(nèi)存損壞的風(fēng)險(xiǎn)。

引申一下

雖然本文主題是終止線程,但其實(shí)可以推廣到所有while(true)的場(chǎng)景中。在可能出現(xiàn)死循環(huán)或者有條件的大循環(huán)中,我們都應(yīng)該適當(dāng)?shù)脑黾訕?biāo)記位,可以快速終止循環(huán)。

比如,批量刷數(shù)據(jù)場(chǎng)景,通常是在循環(huán)中分頁(yè)查詢數(shù)據(jù),然后批量處理這部分?jǐn)?shù)據(jù),處理后進(jìn)入下一個(gè)批次。但是如果分頁(yè)較深,或者額運(yùn)行時(shí)間較長(zhǎng),亦或是有bug,需要快速停止,都可以通過狀態(tài)位實(shí)現(xiàn)。

責(zé)任編輯:武曉燕 來(lái)源: 看山的小屋
相關(guān)推薦

2020-08-03 08:01:50

爬蟲技巧

2020-08-31 06:54:37

注解脫敏ELK

2021-06-28 14:13:34

OOM內(nèi)存事故

2020-04-20 10:47:57

Redis數(shù)據(jù)開發(fā)

2021-04-30 07:33:35

效率提升技巧

2021-04-28 07:22:13

HiveJson數(shù)組

2021-06-26 09:26:01

Jupyter主題目錄

2021-08-04 07:47:18

IDEJTAGSWD

2020-04-19 21:41:13

Python數(shù)據(jù)可視化

2021-03-29 08:24:18

KubeadmKubernetes1運(yùn)維

2010-07-14 10:30:26

Perl多線程

2010-02-02 14:32:32

Python線程編程

2021-11-01 13:55:38

架構(gòu)

2019-03-21 09:45:11

TypeScript編程語(yǔ)言Javascript

2021-04-07 08:13:28

LirbeNMS開源SNMP

2023-11-01 10:49:50

Python面向?qū)ο?/a>

2025-01-16 08:38:34

2023-09-26 12:22:37

隊(duì)列Python

2023-07-31 08:18:50

Docker參數(shù)容器

2011-03-03 10:26:04

Pureftpd
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)