ホームページ >Java >&#&チュートリアル >Javaスレッドが異常終了する問題の解決方法

Javaスレッドが異常終了する問題の解決方法

清浅
清浅オリジナル
2019-04-25 14:18:295829ブラウズ

Java でのスレッドの異常終了の解決策は、まずスレッドの実行中に生成された例外をキャプチャし、次にスレッド例外が発生したときに setUncaughtExceptionHandler メソッドを呼び出して例外をキャッチします。ついに解決します。

Javaスレッドが異常終了する問題の解決方法

#[推奨チュートリアル: Java チュートリアル]

弊社の開発プロジェクトではスレッドがよく使われますが、スレッドを使用すると、次のようなシナリオが考えられます:

#(1) この業務に伴い、比較的時間のかかるタスクが発生し、この業務リターンはタスクを待つ必要はありません。その後、多くの場合、この非同期タスクを完了するためにスレッドを開始します。

(2) データを定期的に消去するなどのスケジュールされたタスクが必要なので、タスクを実行するためにスケジュールされた実行スレッドを開始します。

上記の問題は比較的単純なので、新しいスレッドを作成して実行してください。しかし、問題を見落としがちですが、スレッドが異常な場合はどうすればよいでしょうか?たとえば、時間のかかるタスクの半分しか完了していない場合、そのタスクは異常終了します (ここではトランザクションの一貫性は考慮されていません。タスクが完了する必要があることのみを考慮します)。もう 1 つの例は、データを消去するときにデータベースが切断されることです。この時点で、スレッドが終了し、タスクが終了することがわかります。スケジュールされたタスクを開始するには、プロジェクト全体を再起動する必要があります。

これらの問題を解決する鍵は、スレッドの実行中に生成される例外をどのようにキャプチャするかです。

JDK API を見ると、Thread に setUncaughtExceptionHandler メソッドがあることがわかります。これにより、スレッドで例外が発生したときにこのメソッドを呼び出すことができます。

Java でのスレッドの異常終了の解決策は次のとおりです:

シナリオ 1 の解決策:

public class Plan1 {
    
    private SimpleTask task = new SimpleTask();
    
    public static void main(String[] args) {
        Plan1 plan = new Plan1();
        plan.start();
    }
    public void start(){
        Thread thread = new Thread(task);
        //thread.setDaemon(true); //注释调 否则看不到输出
        thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler(){
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println(e.getMessage());
                start();
            }
        });
        thread.start();
    }
    
    class SimpleTask implements Runnable{
        private int task = 10;
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName+"--"+"启动");
            while(task>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(System.currentTimeMillis()%3==0){
                    throw new RuntimeException("模拟异常");
                }
                System.out.println(threadName+"--"+"执行task"+task);
                task--;
            }
            System.out.println(threadName+"--"+"正常终止");
        }
    }
}

結果出力:

Thread-0--启动
Thread-0--执行task10
Thread-0--执行task9
Thread-0--执行task8
Thread-0--执行task7
模拟异常
Thread-1--启动
Thread-1--执行task6
Thread-1--执行task5
模拟异常
Thread-2--启动
Thread-2--执行task4
Thread-2--执行task3
模拟异常
Thread-3--启动
Thread-3--执行task2
模拟异常
Thread-4--启动
Thread-4--执行task1
Thread-4--正常终止
まだシナリオ 1 ですが、スレッド プール メソッドを見てみましょう。考え方は同じです。なぜシングル スレッドのスレッド プール メソッドを記述するのでしょうか?

public class Plan3 {
    private SimpleTask task = new SimpleTask();
    private MyFactory factory = new MyFactory(task);
    public static void main(String[] args) {
        Plan3 plan = new Plan3();
        ExecutorService pool = Executors.newSingleThreadExecutor(plan.factory);
        pool.execute(plan.task);
        pool.shutdown();
    }
    
    class MyFactory implements ThreadFactory{
        private SimpleTask task;
        public MyFactory(SimpleTask task) {
            super();
            this.task = task;
        }
        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
                @Override
                public void uncaughtException(Thread t, Throwable e) {
                    ExecutorService pool = Executors.newSingleThreadExecutor(new MyFactory(task));
                    pool.execute(task);
                    pool.shutdown();
                }
            });
            return thread;
        }
    }
    
    class SimpleTask implements Runnable{
        private int task = 10;
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName+"--"+"启动");
            while(task>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(System.currentTimeMillis()%3==0){
                    throw new RuntimeException("模拟异常");
                }
                System.out.println(threadName+"--"+"执行task"+task);
                task--;
            }
            System.out.println(threadName+"--"+"正常终止");
        }
    }
}

結果出力:

Thread-0--启动
Thread-0--执行task10
Thread-0--执行task9
Thread-1--启动
Thread-1--执行task8
Thread-2--启动
Thread-2--执行task7
Thread-2--执行task6
Thread-2--执行task5
Thread-2--执行task4
Thread-2--执行task3
Thread-2--执行task2
Thread-3--启动
Thread-3--执行task1
Thread-3--正常终止
ここでは単一のスレッドのみが使用されているため、上記と大きな違いがないことがわかりました。ただし、スレッド プールがスレッド例外をキャッチする方法も示しています。

シナリオ 2 の解決策

次に、シナリオ 2 のスケジュールされたタスクを見てみましょう。単一スレッド プールの例外キャッチ メソッドを作成する必要があるのはなぜですか。以下の比較を行ってください。

スケジュールされたタスクには、上記の ExecutorService の取得方法と同じ ScheduledExecutorService を使用することがよくあります。しかし、上記の方法に従ってスケジュールされたタスクを作成すると、例外が発生します。 uncaughtException メソッドではスレッド例外を取得できないことがわかります。スレッドで例外が発生したときに、例外が消えるか、uncaughtException メソッドがまったく呼び出されなくなります。後で関連する API を確認したところ、ScheduledExecutorService の例外を取得する方法は、ScheduledFuture オブジェクトを使用して取得できることがわかりました。具体的な方法は次のとおりです。 #

public class Plan2 {
    private SimpleTask task = new SimpleTask();
    public static void main(String[] args) {
        Plan2 plan = new Plan2();
        start(plan.task);
    }
    
    public static void start(SimpleTask task){
        ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor();
        ScheduledFuture<?> future = pool.scheduleAtFixedRate(task, 0, 1000, TimeUnit.MILLISECONDS);
        try {
            future.get();
        } catch (InterruptedException | ExecutionException e) {
            System.out.println(e.getMessage());
            start(task);
        }finally {
            pool.shutdown();
        }
    }
    
    class SimpleTask implements Runnable{
        private volatile int count = 0;
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName+"--"+"启动");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(System.currentTimeMillis()%3==0){
                throw new RuntimeException("模拟异常");
            }
            System.out.println(threadName+"--"+"执行task"+count);
            count++;
            System.out.println(threadName+"--"+"正常终止");
        }
    }
}

これまでのところ、それは達成されています。スケジュールされたタスクで例外が発生した場合、それを実行するスレッドが常に存在します。 1 つのスレッドが落ちた場合、後続のスレッドが引き継ぎます。

以上がJavaスレッドが異常終了する問題の解決方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。