Thread 如何安全結束一個線程 MD


Markdown版本筆記 我的GitHub首頁 我的博客 我的微信 我的郵箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

目錄

如何安全的結束一個正在運行的線程

Thread類相關的方法

java.lang.Thread類包含了一些常用的方法,如:start(), stop(), stop(Throwable) ,suspend(), destroy() ,resume()。通過這些方法,我們可以對線程進行方便的操作,但是這些方法中,只有start()方法得到了保留。

在JDK幫助文檔以及Sun公司的一篇文章《Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?》中都講解了舍棄這些方法的原因。

簡單來說是因為:使用stop方法雖然可以強行終止正在運行或掛起的線程,但使用stop方法是很危險的,就象突然關閉計算機電源,而不是按正常程序關機一樣,可能會產生不可預料的結果,因此,並不推薦使用stop方法來終止線程。

那么,我們究竟應該如何停止線程呢?

  • 1、任務中一般都會有循環結構,只要用一個標記控制住循環,就可以結束任務。
  • 2、如果線程處於了凍結狀態,無法讀取標記,此時可以使用interrupt()方法將線程從凍結狀態強制恢復到運行狀態中來,讓線程具備CPU的執行資格。

使用退出標志

當run方法執行完后,線程就會退出。但有時run方法是永遠不會結束的,如在服務端程序中使用線程進行監聽客戶端請求,或是其他的需要循環處理的任務。

在這種情況下,一般是將這些任務放在一個循環中,如while循環。如果想使while循環在某一特定條件下退出,最直接的方法就是設一個boolean類型的標志,並通過設置這個標志為true或false來控制while循環是否退出。

public class Test {
    public static volatile boolean exit = false;//退出標志

    public static void main(String[] args) {
        new Thread() {
            public void run() {
                System.out.println("線程啟動了");
                while (!exit) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("線程結束了");
            };
        }.start();

        try {
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        exit = true;//5秒后更改退出標志的值
    }
}

使用 interrupt 方法

如果一個線程由於等待某些事件的發生而被阻塞,又該怎樣停止該線程呢?

這種情況經常會發生,比如當一個線程由於需要等候鍵盤輸入而被阻塞,或者調用Thread.join()方法,或者Thread.sleep()方法,在網絡中調用ServerSocket.accept()方法,或者調用了DatagramSocket.receive()方法時,都有可能導致線程阻塞,使線程處於處於不可運行狀態時,即使主程序中將該線程的共享變量設置為true,但該線程此時根本無法檢查循環標志,當然也就無法立即中斷。

這時建議不要使用stop()方法,而是使用Thread提供的interrupt()方法,因為該方法雖然不會中斷一個正在運行的線程,但是它可以使一個被阻塞的線程拋出一個中斷異常,從而使線程提前結束阻塞狀態,退出堵塞代碼

使用 interrupt() + InterruptedException

線程處於阻塞狀態,如Thread.sleep、wait、IO阻塞等情況時,調用interrupt方法后,sleep等方法將會拋出一個InterruptedException:

public class Test {
    public static void main(String[] args) {
        Thread thread = new Thread() {
            public void run() {
                System.out.println("線程啟動了");
                try {
                    Thread.sleep(1000 * 100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("線程結束了");
            }
        };
        thread.start();

        try {
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();//作用是:在線程阻塞時拋出一個中斷信號,這樣線程就得以退出阻塞的狀態
    }
}

使用 interrupt() + isInterrupted()

public class Test {
    public static void main(String[] args) {
        Thread thread = new Thread() {
            public void run() {
                System.out.println("線程啟動了");
                while (!isInterrupted()) {
                    System.out.println(isInterrupted());//調用 interrupt 之后為true
                }
                System.out.println("線程結束了");
            }
        };
        thread.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
        System.out.println("線程是否被中斷:" + thread.isInterrupted());//true
    }
}

一個綜合案例

public class Test {
    static boolean flag = true;

    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("開始休眠");
                try {
                    Thread.sleep(100 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("結束休眠,開始死循環");
                while (flag) {
                }
                System.out.println("------------------子線程結束------------------");
            }
        });
        thread.start();

        Scanner scanner = new Scanner(System.in);
        System.out.println("輸入1拋出一個中斷異常,輸入2修改循環標志位,輸入3判斷線程是否阻塞,輸入其他結束Scanner\n");
        while (scanner.hasNext()) {
            String text = scanner.next();
            System.out.println("你輸入了:" + text + "\n");
            if ("1".equals(text)) {
                thread.interrupt();
            } else if ("2".equals(text)) {
                flag = false; //如果不設為false,主線程結束后子線程仍在運行
            } else if ("3".equals(text)) {
                System.out.println(thread.isInterrupted());
            } else {
                scanner.close();
                break;
            }
        }
        System.out.println("------------------主線程結束------------------");
    }
}

不能結束的情況

注意下面這種是根本不能結束的情況!

public class Test {
    public static void main(String[] args) {
        Thread thread = new Thread() {
            public void run() {
                System.out.println("線程啟動了");
                while (true) {//對於這種情況,即使線程調用了intentrupt()方法並且isInterrupted(),但線程還是會繼續運行,根本停不下來!
                    System.out.println(isInterrupted());//調用interrupt之后為true
                }
            }
        };
        thread.start();
        thread.interrupt();//注意,此方法不會中斷一個正在運行的線程,它的作用是:在線程受到阻塞時拋出一個中斷信號,這樣線程就得以退出阻塞的狀態
        while (true) {
            System.out.println("是否isInterrupted:" + thread.isInterrupted());//true
        }
    }
}

2019-5-8


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM