Maison  >  Article  >  Java  >  Comment résoudre le problème de fin anormale du thread Java

Comment résoudre le problème de fin anormale du thread Java

清浅
清浅original
2019-04-25 14:18:295736parcourir

La solution à la fin anormale d'un thread en Java est : capturer d'abord l'exception générée lors de l'exécution du thread ; puis lorsque le thread est anormal, l'exception sera interceptée en appelant la méthode setUncaughtExceptionHandler ; enfin le résoudre.

Comment résoudre le problème de fin anormale du thread Java

[Tutoriel recommandé : Tutoriel Java]

Les threads sont souvent utilisés dans nos projets de développement. Lors de l'utilisation de threads, nous pouvons avoir les scénarios suivants :

(1) Parallèlement à cette activité, une tâche plus longue est générée, et cela. Les retours d'affaires n'ont pas besoin d'attendre la tâche. Ensuite, nous démarrons souvent un thread pour terminer cette tâche asynchrone.

(2) Nous avons besoin d'une tâche planifiée telle que l'effacement régulier des données. Nous démarrerons un thread d'exécution planifié pour effectuer la tâche.

Le problème ci-dessus est relativement simple, créez un nouveau fil de discussion puis faites-le. Mais on néglige souvent un problème, que faire si le fil est anormal ? Par exemple, si l’on ne réalise que la moitié d’une tâche chronophage, on la terminera anormalement (la cohérence des transactions n’est pas considérée ici, on considère seulement que la tâche doit être complétée). Un autre exemple est lors de la suppression des données, la base de données est déconnectée. À ce stade, nous constaterons que le thread meurt et que la tâche est terminée. Nous devons redémarrer l'ensemble du projet pour démarrer la tâche planifiée.

La clé pour résoudre ces problèmes est de savoir comment capturer les exceptions générées lors de l'exécution du thread ?

Lorsque nous examinons l'API JDK, nous constaterons qu'il existe une méthode setUncaughtExceptionHandler dans Thread, qui nous permet d'appeler cette méthode lorsqu'une exception se produit dans le thread.

La solution à la fin anormale d'un thread en Java est :

Solution du scénario 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+"--"+"正常终止");
        }
    }
}

Résultat :

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--正常终止

Toujours dans le scénario 1, jetons un coup d'œil à la méthode du pool de threads. L'idée est la même. Pourquoi écrire une méthode de pool de threads à un seul thread ?

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+"--"+"正常终止");
        }
    }
}

Résultat :

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--正常终止

Comme un seul thread est utilisé ici, j'ai trouvé qu'il n'y avait pas beaucoup de différence par rapport à ce qui précède. Cependant, il montre également comment le pool de threads intercepte les exceptions de thread.

Solution au scénario 2

Regardons maintenant les tâches planifiées du scénario 2. Pourquoi devrais-je écrire une méthode de capture d'exceptions de pool à thread unique, qui est utilisée comme suit : comparaisons.

Nous utilisons souvent ScheduledExecutorService pour les tâches planifiées, ce qui est la même que la méthode d'acquisition ExecutorService ci-dessus. Mais si nous suivons la méthode ci-dessus pour écrire une tâche planifiée, nous obtenons ensuite une exception. Nous constaterons que nous ne pouvons pas obtenir l'exception de thread dans la méthode uncaughtException. L'exception disparaît ou la méthode uncaughtException n'est pas appelée du tout lorsqu'une exception se produit dans le thread. Plus tard, j'ai vérifié l'API correspondante et découvert que le moyen d'obtenir des exceptions dans ScheduledExecutorService peut être obtenu à l'aide de l'objet ScheduledFuture. La méthode spécifique est la suivante :

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+"--"+"正常终止");
        }
    }
}

Sortie du résultat : .

pool-1-thread-1--启动
java.lang.RuntimeException: 模拟异常
pool-2-thread-1--启动
pool-2-thread-1--执行task0
pool-2-thread-1--正常终止
pool-2-thread-1--启动
pool-2-thread-1--执行task1
pool-2-thread-1--正常终止
pool-2-thread-1--启动
pool-2-thread-1--执行task2
pool-2-thread-1--正常终止
pool-2-thread-1--启动
java.lang.RuntimeException: 模拟异常
pool-3-thread-1--启动
pool-3-thread-1--执行task3
pool-3-thread-1--正常终止
pool-3-thread-1--启动
java.lang.RuntimeException: 模拟异常
pool-4-thread-1--启动
pool-4-thread-1--执行task4
pool-4-thread-1--正常终止
.....

Jusqu'à présent, nous avons réalisé que même si une exception se produit dans la tâche planifiée, il y aura toujours un thread pour l'exécuter. Si un thread tombe, les threads suivants prendront le relais.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Article précédent:Qu'est-ce que Javaee ?Article suivant:Qu'est-ce que Javaee ?