JAvaはどのように優雅に原生のThreadスレッドを停止します

8078 ワード

最近は、プロジェクトでフレームアニメーションが使用されているためです.よく知られているように、フレームアニメーションはリソースを消費し、一度関連するピクチャが多いと、OOMが容易になります.
そこでこの問題を解決するために,Surface Viewをカスタマイズし,サブスレッドを用いてフレームごとにbitmapを描画する方式を採用した.このSurfaceで再生する画像リストは設定可能です.つまり、前のアニメーションがまだ放送されていないかもしれないので、新しい画像リストを設定しました.これにより、古いアニメーションスレッドを停止し、新しいスレッドを開いて新しいアニメーションを再生する必要があります.そこでタイトルのこの問題に関連しました:javaはどのように優雅に原生のThreadスレッドを停止します.
Threadクラスには、スレッドを停止するためのstopメソッドがあることはよく知られています.では、この方法を直接使ってもいいですか?答えはだめです.stop方法はすでに公式に明確に廃棄されているからだ.主な原因は、この方法があまりにも簡単で乱暴だからだ.少なくとも以下の問題が発生します.
(1)stop法はスレッド操作の原子性を破壊する.たとえば、スレッドの操作には同期が必要です.後でいくつかの重要な方法が実行されるか、リソースを解放しなければならない可能性があります.このときstopメソッドを呼び出してスレッドを停止し、彼は何が同期しているかどうかにかかわらず、このプロセスが完全に完了しているかどうかを確認します.直接止めてあげます.
以上の理由からstopメソッドを呼び出すと、UnsupportedOperationExceptionの例外が直接受信されます.
stopメソッドが使えない以上.他に何かできる方法はありませんか?友達がThread類の中にinterruptの方法があることを覚えていて、interruptは中断ではありませんか?では、私はこの方法を呼び出せばいいのではないでしょうか.
ここではまず3つの明確な結論を直接出しましょう.
(1)この方法の名前には迷いがある.
(2)この方法は実際には1つのスレッドを停止することはできない.
(3)我々が書いたスレッドがこの方法で停止した場合があるが,そうではなく,このスレッドを停止した根本的な原因は異常である.
次の2つの例を示します.
(1)
    private Thread mThread;
    private boolean mIsRunning = true;

    private void startThread(){
        mThread = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        int i = 0;
                        try {
                            while(mIsRunning) {
                                Log.d(
                                        "MyThreadDemo",
                                        String.valueOf(i)
                                );
                                mThread.sleep(1000);
                                i++;
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }finally {
                            mustDoThings();
                        }
                    }
                }
        );
        mThread.start();
    }

    private void interruptThread(){
        if(mThread!=null){
            mThread.interrupt();
        }
    }

    private void mustDoThings(){
        Log.d("MyThreadDemo","        ");
    }

まずstartThreadメソッドを呼び出してスレッドを開き、スレッドがしばらく走るのを待っています.その後interruptThreadメソッドを呼び出します.ログを見てみましょう
06-27 17:56:18.762 2480-2935/com.wm.mythreaddemo D/MyThreadDemo: 4
06-27 17:56:19.763 2480-2935/com.wm.mythreaddemo D/MyThreadDemo: 5
06-27 17:56:20.764 2480-2935/com.wm.mythreaddemo D/MyThreadDemo: 6
06-27 17:56:21.764 2480-2935/com.wm.mythreaddemo D/MyThreadDemo: 7
06-27 17:56:22.765 2480-2935/com.wm.mythreaddemo D/MyThreadDemo: 8
06-27 17:56:23.766 2480-2935/com.wm.mythreaddemo D/MyThreadDemo: 9
06-27 17:56:24.766 2480-2935/com.wm.mythreaddemo D/MyThreadDemo: 10
06-27 17:56:25.433 2480-2935/com.wm.mythreaddemo W/System.err: java.lang.InterruptedException
        at java.lang.Thread.sleep(Native Method)
06-27 17:56:25.434 2480-2935/com.wm.mythreaddemo W/System.err:     at java.lang.Thread.sleep(Thread.java:1031)
        at java.lang.Thread.sleep(Thread.java:985)
        at com.wm.mythreaddemo.MainActivity$1.run(MainActivity.java:74)
        at java.lang.Thread.run(Thread.java:818)
06-27 17:56:25.434 2480-2935/com.wm.mythreaddemo D/MyThreadDemo:         

スレッドが止まったんじゃないの?そしてfinallyで呼び出さなければならないコードmustDoThingsメソッドも正常に呼び出されました.しかし、もっとよく見ると、finallyの前にInterruptedExceptionが投げ出されていることがわかります.だから、今回のスレッドの停止とmustDoThingsメソッドの呼び出しは完全にこの異常のためです.    
    private Thread mThread;
    private boolean mIsRunning = true;

    private void startThread(){
        mThread = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        int i = 0;
                        while(mIsRunning) {
                            Log.d(
                                    "MyThreadDemo",
                                    String.valueOf(i)
                            );
                            i++;
                        }
                    }
                }
        );
        mThread.start();
    }

    private void interruptThread(){
        if(mThread!=null){
            mThread.interrupt();
        }
    }

私たちはまずstartThread、それからinterruptThreadです.今回どのようにinterruptThreadメソッドを呼び出しても、このスレッドを止めることはできません.
実はinterruptメソッドは、スレッドを停止する識別ビットを設定するだけです.スレッドがブロックされている場合、interruptメソッドを呼び出すとInterruptException異常が放出され、停止スレッドの識別ビットが消去されます.
stopが使えないことを知った以上、interruptメソッドはスレッドを直接停止することはできません.スレッドを停止する正しい姿勢は何ですか?ここでは、一般的な2つを提供します.
(1)停止スレッドの識別ビットの自己保守
    private Thread mThread;
    private boolean mIsRunning = true;

    private void startThread(){
        mThread = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        int i = 0;
                        while(mIsRunning){
                            Log.d(
                                    "MyThreadDemo",
                                    String.valueOf(i)
                            );
                            try {
                                mThread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            i++;
                        }
                        mustDoThings();
                    }
                }
        );
        mThread.start();
    }

    private void stopBoolThread(){
        mIsRunning = false;
    }

    private void mustDoThings(){
        Log.d("MyThreadDemo","        ");
    }
これは、whileループが実行を継続する必要があるかどうかを制御するために、識別ビットを完全に自分で維持することである.続行しない場合は、whileの後に必要な操作の処理を行います.
(2)interruptメソッドで設定した識別ビット
Threadが識別ビットの設定を提供する以上,この方法を用いて実現することができる.Threadはまた、この識別ビットを取得するためのisInterruptedメソッドを提供する.
    private Thread mThread;
    private void startThread(){
        mThread = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        int i = 0;
                        try {
                            while(!mThread.isInterrupted()){
                                Log.d(
                                        "MyThreadDemo",
                                        String.valueOf(i)
                                );
                                mThread.sleep(1000);
                                i++;
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } finally {
                            mustDoThings();
                        }
                    }
                }
        );
        mThread.start();
    }

    private void interruptThread(){
        if(mThread!=null){
            mThread.interrupt();
        }
    }

try-catchの小包範囲の問題に注意してください.前に述べたように、ブロックされたスレッドに対してinterruptメソッドを呼び出すと、スレッドを停止する識別ビットが消去され、この識別ビットがfalseになるからです.以下のコードを書いたらinterruptメソッドではこのスレッドを止められません.
エラーのデモ:
private void startThread(){
        mThread = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        int i = 0;
                        while(!mThread.isInterrupted()){
                            Log.d(
                                    "MyThreadDemo",
                                    String.valueOf(i)
                            );
                            try {
                                mThread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            i++;
                        }
                        mustDoThings();
                    }
                }
        );
        mThread.start();
}