一塊兒學併發編程 - 優雅關閉

Java中原來在Thread中提供了stop()方法來終止線程,但這個方法是不安全的,因此通常不建議使用。文本將介紹兩種能夠優雅的終止線程的方式...java

<!-- more -->git

第一種

在JAVA《Java多線程模式》中有一種叫Two-Phase Termination(兩步終止)的模式能夠優雅的終止線程,這種模式採用了兩個步驟來終止線程,因此叫兩步終止模式安全

  • 先將執行標誌位isShutdown 設爲false,使工做中的線程轉變爲終止處理中的狀態
  • 真正去執行終止操做,這樣的作法能夠保證線程的安全性、生命性和響應性。
class Worker extends Thread {
    private volatile boolean isShutdown = true;

    public void shutdown() {
        System.out.println("接收到關閉通知......");
        this.isShutdown = false;
        interrupt();
    }

    @Override
    public void run() {
        while (this.isShutdown) {
            System.out.println("正在工做:" + System.currentTimeMillis());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("打斷正在工做的線程......");
            }
        }
        System.out.println("銷燬......");
    }
}

public class ThreadClose {
    public static void main(String[] args) throws InterruptedException {
        Worker worker = new Worker();
        worker.start();//開始工做
        Thread.sleep(3 * 1000);
        worker.shutdown();//優雅關閉
    }
}

運行日誌微信

正在工做:1505828036769
正在工做:1505828037770
正在工做:1505828038771
接收到關閉通知......
打斷正在工做的線程......
銷燬......
  • 安全性:不會在線程正在執行關鍵區域--Critical Section的時候忽然結束掉
  • 生命性:必定會進行終止處理,shutdown()中,會調用interrupt(),保證即便線程處於sleepwait狀態也能夠被當即終止
  • 響應性:將isShutdown 設爲volatile ,能保證線程收到終止請求後,會盡快開始終止處理。

存在的問題:針對沒有阻塞的狀況:設置標誌變量,讓線程正常天然死亡,和諧!,可是若是在調用shutdown發生阻塞狀況呢?多線程

第二種

《多線程第一章》的時候,介紹過守護線程的做用,那麼是否是能夠經過開啓守護線程的方式去監聽ide

功能

1.當工做結束就關閉主線程(主線程銷燬守護線程也會跟着一同銷燬)
2.若是任務長時間未完成,中止工做任務,減小開銷測試

編碼

1.定義主線程與發送的指令
2.在主線程run方法中建立一個守護線程,用來執行咱們投遞的任務
3.前面已經介紹過join的功能,它能夠阻塞主線程,等待子線程完成後主線程繼續執行
4.若是join釋放後,發送完成指令this

private Thread executeService;
private volatile boolean finished = false;
public void execute(Runnable task) {
    executeService = new Thread(() -> {
        Thread runner = new Thread(task);
        runner.setDaemon(true);
        runner.start();
        try {
            runner.join();//前面已經說過join與線程了
            finished = true;
        } catch (InterruptedException e) {
            System.out.println("打斷正在工做的線程......");
        }
    });
    executeService.start();
}

5.建立listener(long mills),監聽工做狀況
6.監放任務是否完成,若是未完成監聽當前是否逾期,逾期打斷線程結束監聽編碼

public void listener(long mills) {
    System.out.println("開啓監聽......");
    long currentTime = System.currentTimeMillis();
    while (!finished) {
        if ((System.currentTimeMillis() - currentTime) >= mills) {
            System.out.println("工做耗時過長,開始打斷...");
            executeService.interrupt();//打斷線程
            break;
        }
        try {
            executeService.sleep(100L);//每隔100毫秒檢測一次
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

7.測試線程

public static void main(String[] args) {
    WorkerService service = new WorkerService();
    long start = System.currentTimeMillis();
    service.execute(() -> {
        try {
            Thread.sleep(3 * 1000);// TODO 模擬加載數據
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    service.listener(4 * 1000);
    System.out.println("一共耗時:" + (System.currentTimeMillis() - start));
}

listener(4 * 1000) 的運行日誌,當任務完成會直接退出,並不會一直佔用

開啓監聽......
一共耗時:3049

listener(2 * 1000) 的運行日誌,當任務超時直接打斷線程,減小資源佔用

開啓監聽......
工做耗時過長,開始打斷...
一共耗時:2050
打斷正在工做的線程......

- 說點什麼

全文代碼:https://gitee.com/battcn/battcn-concurent/tree/master/Chapter1-1/battcn-thread/src/main/java/com/battcn/chapter4

  • 我的QQ:1837307557
  • battcn開源羣(適合新手):391619659

微信公衆號:battcn(歡迎調戲)

相關文章
相關標籤/搜索