Java多線程:3.多線程的停止

一、如何正確的停止線程

使用interrupt來通知,而不是強制。
interrupt,即中斷,需要停止線程時,需要另外一個線程向該線程通知你該中斷了,而何時中斷,停不停止,是由該線程自己決定的,外部並沒有方法直接操作它。
線程何時會停止:

  • run()方法中的代碼都已經執行完畢了。
  • 線程進行業務邏輯處理時拋出異常了。

二、使用interrupt停止線程

1.普通情況下停止線程

先嚐試使用thread.interrupt()中斷該線程。

/**
 * 普通情況下停止線程
 */
public class StopThreadTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Work());
        thread.start();

        // 線程啓動後1s,通知中斷
        Thread.sleep(1000);
        thread.interrupt();
    }
}

// 任務:打印出小於999999999的所有1000的倍數的數
class Work implements Runnable{
    @Override
    public void run() {
        for(int num=0; num < 999999999; num ++){
            if(num % 1000 == 0){
                System.out.println(num);
            }
        }
        System.out.println("任務完成");
    }
}

發現即使調用了thread.interrupt(),線程並沒有中斷。因爲線程中沒有對這個中斷進行響應,需要在線程中添加一些響應中斷的代碼:

// 任務:打印出小於999999999的所有1000的倍數的數
class Work implements Runnable{
    @Override
    public void run() {
        for(int num=0; num < 999999999; num ++){
            // 判斷中斷
            if(Thread.currentThread().isInterrupted()){
                break;
            }

            if(num % 1000 == 0){
                System.out.println(num);
            }
        }
        System.out.println("任務完成");
    }
}
2.在阻塞的情況下停止線程

線程在sleep阻塞時,收到主線程發來的中斷通知:

/**
 * 線程中有sleep阻塞的情況下停止線程
 */
public class StopThreadTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Work());
        thread.start();

        // 線程啓動後1s,通知中斷
        Thread.sleep(500);
        thread.interrupt();
    }
}

// 任務:打印出小於300的數
class Work implements Runnable{
    @Override
    public void run() {
        for(int num=0; num < 300; num ++){
            // 判斷中斷
            if(Thread.currentThread().isInterrupted()){
                break;
            }

            System.out.println(num);
        }

        // 阻塞等待其他業務邏輯
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("任務完成");
    }
}

會發現拋出了異常,即sleep時收到中斷
在這裏插入圖片描述

3.在每次迭代後都阻塞的情況下停止線程

每次迭代都有sleep阻塞,而阻塞的特點就是它本身就能響應中斷而拋出異常去停止線程,這種情況下我們就不需要手動去寫響應中斷的代碼了。

/**
 * 線程中每次循環都會有sleep阻塞的情況下停止線程
 */
public class StopThreadTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Work());
        thread.start();

        // 線程啓動後1s,通知中斷
        Thread.sleep(2000);
        thread.interrupt();
    }
}

// 任務:打印出小於10000的數
class Work implements Runnable{
    @Override
    public void run() {
        try {
            for(int num=0; num < 10000; num ++){
                System.out.println(num);

                Thread.sleep(10);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("任務完成");
    }
}

在這裏插入圖片描述

4.sleep會清除中斷標誌的問題

如果 try{ sleep }catch 放在循環內,當sleep響應中斷後,會清除中斷標誌位,所以即使我們使用Thread.currentThread().isInterrupted()來手動響應中斷,也不會停止線程。

// 任務:打印出小於10000的數
class Work implements Runnable{
    @Override
    public void run() {
        for(int num=0; num < 10000; num ++){
            // 手動響應
            if(Thread.currentThread().isInterrupted()){
                break;
            }

            System.out.println(num);

            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        System.out.println("任務完成");
    }
}

在這裏插入圖片描述


三、實際開發中最好的兩種停止線程的方法

1.傳遞中斷
2.恢復中斷
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章