停止及中斷運行的線程

停止線程的方式

在 Java 中停止線程的方式有三種:
1、線程的 run 方法執(zhí)行到最后,線程正常結(jié)束并退出;
2、使用 Thread 類提供的 stop 方法去停止一個正在運行的線程,該方法由于存在安全問題已被標記為過時,不推薦使用了;
3、使用 Java 中推薦的方式,即使用線程中斷機制,去中斷線程;

三種方式介紹

1、run 方法自行結(jié)束,這個是線程正常執(zhí)行完后結(jié)束,沒什么值得說的;

2、Thread 類的 stop 方法;

這個是一個在 Java 中被標記為過時的方法,因為該方法有線程安全方面的風(fēng)險,因此已經(jīng)不推薦使用了;具體原因可以參考源碼中的解釋,已經(jīng)寫的非常清楚了,下面舉個例子說明:

public static void main(String[] args) throws InterruptedException {
        TestObject object = new TestObject(1, 1);
        TestRunnable testRunnable = new TestRunnable(object);
        Thread t1 = new Thread(testRunnable);
        Thread t2 = new Thread(testRunnable);
        t1.start();
        t2.start();
//        t1.stop();
        t1.join();
        t2.join();
        System.out.println(object.getX() == object.getY());
        System.out.println(object.getX() );
        System.out.println(object.getY());
        System.out.println("main end");
    }

    private static class TestObject{
        private int x ;
        private int y ;

        TestObject(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public int getX() {
            return x;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return y;
        }

        public void setY(int y) {
            this.y = y;
        }
    }

    private static class TestRunnable implements Runnable {
        private final Object lock = new Object();
        private final TestObject object;
        TestRunnable(TestObject object) {
           this.object = object;
        }
        @Override
        public void run() {
            synchronized (lock){
                object.setX(object.getX() + 1);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
               object.setY(object.getY() + 1);
            }
        }
    }

結(jié)果

false
3
2

如上 demo,基本上可以說明 stop 的線程安全問題了。在上面的示例中,runnable 任務(wù)分別將對象 TestObject 中的屬性 x 和屬性 y 加1,中間休息了 100 毫秒,模擬執(zhí)行其他操作,在正常情況下他們是同時加1的。在不調(diào)用 stop 方法時,兩個屬性結(jié)果一致,都是3,但是當(dāng)調(diào)用線程 t1 調(diào)用了 stop 方法,屬性 x 值加1了,由于 y 得值還沒來得及加 1 線程停止了,導(dǎo)致本來是原子的操作兩個屬性,最后沒能一起增加,導(dǎo)致了線程安全的問題。
因此 stop 方法在 Java 中標記為過時,并且也不推薦使用了。現(xiàn)在推薦的是使用線程的中斷機制。

3、線程的中斷機制介紹;

Java 中提供的線程中斷機制,實際上是一種線程之間的協(xié)作方式;這種中斷和 stop 有著本質(zhì)的區(qū)別,stop 是直接中斷線程,而 Java 中的中斷機制,其實是給線程設(shè)置一個中斷的標識,當(dāng)前線程并不會被真的終止執(zhí)行,因此需要線程自己去根據(jù)的標識處理相關(guān)邏輯。下面介紹線程中斷機制中,三個重要的方法。

a、interrupt 方法

這個方法可以給線程設(shè)置中斷的標識,然后可以根據(jù)中斷的標識自行處理。例如線程 A 和主線程,主線程調(diào)用線程 A 的 interrupt 方法,這個時候就把線程 A 的中斷標識設(shè)為了 true,但是這個時候線程 A 仍在執(zhí)行,需要線程 A 根據(jù)標識做處理,結(jié)束自己的執(zhí)行。

public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(() -> {
            while(true) {
                System.out.println("threadA is running");
                //如果中斷標識為true,則退出循環(huán),threadA 執(zhí)行結(jié)束
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("threadA is stop");
                    break;
                }
            }
        });
        threadA.start();
        Thread.sleep(100);
        threadA.interrupt();
        threadA.join();
        System.out.println("main is end");
    }

結(jié)果

threadA is running
threadA is running
threadA is running
threadA is running
threadA is running
threadA is running
threadA is stop
main is end

在調(diào)用 interrupt 方法時,還有一個需要注意的,當(dāng)線程內(nèi)調(diào)用了 wait、sleep、join 方法而被掛起時,當(dāng)前線程會在這些地方拋出,InterruptedException 異常后返回。例如如果線程 A 中有調(diào)用 wait 或者 sleep 或者 join 方法,這個時候主線程調(diào)用了 interrupt 方法,則線程 A 會在調(diào)用這些方法的地方,拋出 InterruptedException 異常,需要程序自己處理該異常,保證線程安全切正確的執(zhí)行。

public static void main(String[] args) throws InterruptedException {
        TestObject object = new TestObject(1, 1);
        TestRunnable testRunnable = new TestRunnable(object);
        Thread t1 = new Thread(testRunnable);
        Thread t2 = new Thread(testRunnable);
        t1.start();
        t2.start();
        t1.interrupt();
        t1.join();
        t2.join();
        System.out.println(object.getX() == object.getY());
        System.out.println(object.getX() );
        System.out.println(object.getY());
        System.out.println("main end");
    }

    private static class TestObject{
        private int x ;
        private int y ;

        TestObject(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public int getX() {
            return x;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return y;
        }

        public void setY(int y) {
            this.y = y;
        }
    }

    private static class TestRunnable implements Runnable {
        private final Object lock = new Object();
        private final TestObject object;
        TestRunnable(TestObject object) {
           this.object = object;
        }
        @Override
        public void run() {
            synchronized (lock){
                int x = object.getX();
                int y = object.getY();
                try {
                    object.setX(x + 1);
                    Thread.sleep(100);
                    object.setY(y + 1);
                } catch (InterruptedException e) {
                    object.setY(y);
                    object.setX(x);
                }
            }
        }
    }

結(jié)果

true
2
2

上面示例使用了說明 stop 不安全的例子,稍微修改了代碼,由于示例中有調(diào)用 sleep 方法,因此處理了 InterruptedException 異常,將 stop 改為了 interrupt 方法,從而避免了線程不安全的問題,該示例了沒有對線程的中斷標識做處理,因為該示例處理的意義不大,這個根據(jù)具體使用場景處理就行。

b、isInterrupted 方法

這個方法用于檢查當(dāng)前線程的中斷標識,如果當(dāng)前線程標識為中斷,則返回 true 反之則返回 false,這個方法的使用方式,在上面的示例中已經(jīng)給出,這里就不提供具體的代碼了。

c、interrupted 方法

這個方法也是用于檢查線程的中斷標識,如果中斷則返回 true,反之則是 false。但是和 isInterrupted 方法有兩點不同;第一如果發(fā)現(xiàn)線程被中斷會返回 true,同時會清除線程的中斷標識,這個時候線程標識變成了非中斷;第二這個方法獲取的是當(dāng)前執(zhí)行線程的線程狀態(tài),并不是被調(diào)用線程的狀態(tài),具體看下面的代碼;

/**
     * Tests whether the current thread has been interrupted.  The
     * <i>interrupted status</i> of the thread is cleared by this method.  In
     * other words, if this method were to be called twice in succession, the
     * second call would return false (unless the current thread were
     * interrupted again, after the first call had cleared its interrupted
     * status and before the second call had examined it).
     *
     * <p>A thread interruption ignored because a thread was not alive
     * at the time of the interrupt will be reflected by this method
     * returning false.
     *
     * @return  <code>true</code> if the current thread has been interrupted;
     *          <code>false</code> otherwise.
     * @see #isInterrupted()
     * @revised 6.0
     */
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

上面是該方法的 JDK 源碼,它返回的是當(dāng)前執(zhí)行的線程的中斷狀態(tài);

public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(() -> {
            System.out.println("threadA start interrupted :" + Thread.currentThread().isInterrupted());
            while (!Thread.interrupted()) {
            }
            System.out.println("threadA stop interrupted :" + Thread.currentThread().isInterrupted());
        });
        threadA.start();
        threadA.interrupt();
        threadA.join();
        System.out.println("main end");
    }

結(jié)果

threadA start interrupted :true
threadA stop interrupted :false
main end

總結(jié)

1、stop 是線程不安全的方法,不推薦使用;
2、interrupt 只是設(shè)置一個中斷的標識,需要在線程里面處理相關(guān)邏輯;
3、interrupt 執(zhí)行時,有 sleep、wait、join、的地方會拋出 InterruptedException 異常;
4、interrupted 和 isInterrupted 都可以獲取線程的中斷狀態(tài);
5、interrupted 獲取的是當(dāng)前線程的中斷狀態(tài),并且會清除中斷標識;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。