面試題:如何優雅的停止一個正在運行的java線程

線程狀態轉換圖:

幾個容易混淆概念的方法

Thread.sleep()、 Thread.join() 或 Object.wait(),
他們都是阻塞方法並聲明拋出 InterruptedException.
當線程調用這些方法進入阻塞狀態後, 如果外部調用了interrupt(),
線程進入就緒狀態, 並且這些方法拋出InterruptedException異常.

public void Thread.interrupt() // 設置線程的中斷狀態flag.
public boolean Thread.isInterrupted() // 獲取線程的中斷狀態flag值
public static boolean Thread.interrupted() // 取線程的中斷狀態flag值,並重置flag值爲false.
public final void join() throws InterruptedException  //指的是調用者進入阻塞狀態, 而不是線程對象進入阻塞狀態, 線程對象繼續在執行, 不受影響, 看下面的例子.
public static void yield(); //線程從運行狀態, 回到就緒狀態.
//demo: join()
public class BottomBarManager {
    private final Object handlePopupMenuAction(int actionCode, Object... args) {
    switch (actionCode) {
            case Actions.MenuContainer.DOWNLOAD: // 下載
                ThreadTest tt = new ThreadTest();
                tt.start();
                try {
                    tt.join();  //當前線程等待tt線程執行完再往下執行
                    //作用是, 當前的UI線程進入阻塞狀態, 直到等tt線程執行完再往下繼續執行, tt線程該幹什麼還幹什麼, 不受影響.
                    //千萬不要誤理解成tt線程進入了阻塞狀態.
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                JLog.i();
            break;


    }


    private class ThreadTest extends Thread {


        @Override
        public void run() {
            try {
                sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            JLog.i();
        }
    }


}

中斷線程的最佳實踐是:  中斷 + 條件變量。

最佳模板:

public class BestPractice extends Thread {
    private volatile boolean finished = false;   // ① volatile條件變量
    public void stopMe() {
        finished = true;    // ② 發出停止信號
        interrupt();
    }
    @Override
    public void run() {
        while (!finished) {    // ③ 檢測條件變量
        try {
                // do dirty work   // ④業務代碼
        } catch (InterruptedException e) {
                if (finished) {
                    return;
                }
                continue;
        }
        }
    }
}

當④處的代碼阻塞於wait()或sleep()時,線程不能立刻檢測到條件變量。因此②處的代碼最好同時調用interrupt()方法。

典型的體現是在volley項目中。
// CacheDispatcher.java
public class CacheDispatcher extends Thread {
    private volatile boolean mQuit = false;


    public void quit() {
        mQuit = true;
        interrupt();
    }


    @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);


        // Make a blocking call to initialize the cache.
        mCache.initialize();


        while (true) {
            try {
                // Get a request from the cache triage queue, blocking until
                // at least one is available.
                final Request<?> request = mCacheQueue.take(); //****** block方法, it throws InterruptedException;
                request.addMarker("cache-queue-take");


                // Attempt to retrieve this item from cache.
                Cache.Entry entry = mCache.get(request.getCacheKey());
                if (entry == null) {
                    request.addMarker("cache-miss");
                    // Cache miss; send off to the network dispatcher.
                    mNetworkQueue.put(request);
                    continue;
                }


                // We have a cache hit; parse its data for delivery back to the request.
                request.addMarker("cache-hit");
                Response<?> response = null;
                try {
                    response = request.parseNetworkResponse(
                            new NetworkResponse(entry.data, entry.responseHeaders));
                } catch (Exception e) {
                    mDelivery.postError(request, new ParseError(e));
                    continue;
                } catch (Error error) {
                    mDelivery.postError(request, new ParseError(error));
                    continue;
                }
                request.addMarker("cache-hit-parsed");


            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            } catch (OutOfMemoryError error) {
                if (mQuit) {
                    return;
                }
                continue;
            }
        }
    }


}

在quit()代碼裏, 設置條件變量, 同時調用interrupt()方法.

以後寫線程類時, 參考這個模板寫就很優雅和規範。

中斷是禮貌地請求另一個線程在它願意並且方便的時候停止它正在做的事情。

喜歡就點個"在看"唄,留言、轉發朋友圈

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