1. 程式人生 > >java執行緒中斷機制

java執行緒中斷機制

利用標誌變數

通過檢查volatile型別的標誌變數控制執行緒中斷,不使用volatile型別的標誌變數會導致jvm對記憶體的優化引起死迴圈

public class StopThread {
    private static volatile boolean stopRequested;

    public static void main(String[] args) throws InterruptedException {
        Thread backgroundThread = new Thread(new Runnable() {
            public
void run() { int i = 0; while (!stopRequested) { i++; } } }); backgroundThread.start(); TimeUnit.SECONDS.sleep(1); stopRequested = true; } }

interrupt中斷

如果不通過像上述這樣的標誌變數中斷執行緒,也可以利用Thread類的相關方法進行中斷

header 1 header 2
public static boolean interrupted 測試當前執行緒是否已經中斷。執行緒的中斷狀態 由該方法清除。換句話說,如果連續兩次呼叫該方法,則第二次呼叫將返回 false(在第一次呼叫已清除了其中斷狀態之後,且第二次呼叫檢驗完中斷狀態前,當前執行緒再次中斷的情況除外)。
public boolean isInterrupted() 測試執行緒是否已經中斷。執行緒的中斷狀態不受該方法的影響。
public void interrupt() 中斷執行緒。

Thread.interrupt方法可以中斷執行緒正在執行的會丟擲InterruptedException的方法時,執行interrupt方法的時候該執行緒會丟擲異常,以異常的方式中斷執行緒,比如下面這個例子

例一

public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(60);
                } catch (InterruptedException e) {
                    //執行後續清理
                    System.err.println("this thread has been notified...");
                }

            }
        });
        thread.start();
        //等待讓thread執行緒開始執行
        TimeUnit.SECONDS.sleep(1);
        thread.interrupt();
        System.out.println("finish");
    }

sleep方法會丟擲InterruptedException,sleep方法是一個可中斷的方法,上面這個例子thread執行緒處於sleep狀態時,mian執行緒呼叫interrupt方法中斷了正在睡眠的執行緒.在catch中務必進行資源的清理工作,thread.interrupt()方法不能中斷正在等待鎖或者正在等待I/O的執行緒.如果想要關閉I/O等待的資源可以直接關閉底層資源來終止執行緒,而對於鎖等待的執行緒終止在下面會有案例.在這個例子中尤其要注意一點,InterruptedException異常被捕獲之後,中斷狀態會被重設為false

例二

Java中斷機制是一種協作機制,也就是說通過中斷並不能直接終止另一個執行緒,而需要被中斷的執行緒自己處理中斷。在下面這個例子中,我們檢查Thread類的標誌變數的方法來中斷執行緒

class Compute implements Runnable {

    @Override
    public void run() {
        double d = 1.0;

        while (!Thread.interrupted()) {

            d = d + (Math.PI + Math.E) / d;

        }
    }
}

public class NonInterrupTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Compute());
        thread.start();
        TimeUnit.SECONDS.sleep(2);
        thread.interrupt();
        System.out.println("interrupt...");
    }
}

再強調下上文提到的一點Thread.interrupted()方法在呼叫之後Thread的標誌變數會被複位

ReentrantLock

上文提到對於正在進行鎖等待的執行緒,我們無法通過Thread.interrupt方法立即中斷執行緒,那麼有沒有一種鎖可以在被interrupt時中斷鎖等待呢?實際上我們可以通過lockInterruptibly方法實現


class BlockMutex {
    private Lock lock = new ReentrantLock();

    public BlockMutex() {
        lock.lock();
    }

    public void f() {
        try {
            lock.lockInterruptibly();
            System.out.println("lock acquire in f() ");
        } catch (InterruptedException e) {
            //資源清理
            e.printStackTrace();
        }
    }

}

class MyBlockThread implements Runnable {

    private BlockMutex blockMutex;

    public MyBlockThread(BlockMutex blockMutex) {
        this.blockMutex = blockMutex;
    }

    @Override
    public void run() {
        System.out.println("waiting lock in f()");
        blockMutex.f();
        System.out.println("broken out of blocked call");
    }
}

public class MyLockInterruptTest {
    public static void main(String[] args) throws InterruptedException {
        //執行緒建立BlockThread同時就持有了blockMutex鎖
        BlockMutex blockMutex = new BlockMutex();
        Thread thread = new Thread(new MyBlockThread(blockMutex));
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        //中斷鎖等待
        thread.interrupt();
    }
}

主執行緒在建立BlockMutex時就持有物件鎖,另一執行緒在等待獲取BlockMutex物件鎖時是就中斷該執行緒