Grupo de subprocesos: detiene los subprocesos en el grupo de subprocesos, maneja correctamente las excepciones de los subprocesos en el grupo de subprocesos

Grupo de subprocesos propio de Java

Java proporciona una herramienta muy útil para la clase Executors, a través de Executors podemos crear fácilmente una serie de subprocesos:

Executors.newCachedThreadPool, se puede crear un grupo de subprocesos nuevos según sea necesario. El subproceso creado en el grupo de subprocesos se puede utilizar para completar otra tarea después de completar una tarea.

Executors.newFixedThreadPool (int nThreads), para crear un grupo de subprocesos que pueda reutilizar un número fijo de subprocesos. Este grupo de subprocesos contiene como máximo subprocesos nThread.

Executors.newSingleThreadExecutor (), crea un Ejecutor usando un solo hilo de trabajo. Incluso si hay muchas tareas, solo se usa un hilo para completar la tarea.

Executors.newSingleThreadScheduledExecutor (), crea un programa de ejecución de un solo subproceso, que puede programarse para ejecutar comandos después de un retraso determinado o ejecutarse periódicamente.

Los amigos que necesiten más conocimientos de Java y preguntas de la entrevista pueden hacer clic en el enlace de abajo para obtenerlo gratis

Enlace: 1103806531 Contraseña: CSDN

Inserte la descripción de la imagen aquí

Si el hilo enviado al grupo de hilos se puede interrumpir

El grupo de subprocesos ExecutorService proporciona dos métodos convenientes para detener los subprocesos en el grupo de subprocesos, son shutdown y shutdownNow.

Shutdown no aceptará nuevas tareas, pero esperará a que se completen las tareas existentes. Y shutdownNow intentará terminar inmediatamente los hilos en ejecución existentes.

Entonces, ¿cómo se logra? Observamos una implementación en un ThreadPoolExecutor:

    public List<Runnable> shutdownNow() {
    
    
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
    
    
            checkShutdownAccess();
            advanceRunState(STOP);
            interruptWorkers();
            tasks = drainQueue();
        } finally {
    
    
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }

Hay una llamada al método interruptWorkers (), que en realidad es para interrumpir el hilo en ejecución.

Entonces podemos llegar a la conclusión de que la tarea enviada a ExecutorService debe ser interrumpible, de lo contrario, el método shutdownNow fallará.

    public void correctSubmit(){
    
    
        Runnable runnable= ()->{
    
    
            try(SocketChannel  sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8080))) {
    
    
                ByteBuffer buf = ByteBuffer.allocate(1024);
                while(!Thread.interrupted()){
    
    
                    sc.read(buf);
                }
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        };
        ExecutorService pool =  Executors.newFixedThreadPool(10);
        pool.submit(runnable);
        pool.shutdownNow();
    }

Necesitamos agregar un juicio de interrupción en el ciclo while para controlar la ejecución del programa.

Manejar correctamente las excepciones en subprocesos en el grupo de subprocesos

Si se produce una excepción en un hilo del grupo de hilos, como RuntimeException, ¿cómo podemos detectarlo? Si la excepción no se puede manejar de manera razonable, ocurrirán problemas impredecibles.

Mira el siguiente ejemplo:

    public void wrongSubmit() throws InterruptedException {
    
    
        ExecutorService pool = Executors.newFixedThreadPool(10);
        Runnable runnable= ()->{
    
    
            throw new NullPointerException();
        };
        pool.execute(runnable);
        Thread.sleep(5000);
        System.out.println("finished!");
    }

En el ejemplo anterior, enviamos una tarea y se lanzará una NullPointerException en la tarea. Debido a que es una excepción no marcada, no es necesario capturarla explícitamente. Una vez completada la tarea, básicamente no podemos saber si la tarea se ha ejecutado correctamente. Arriba.

Entonces, ¿cómo podemos detectar tales excepciones de grupos de subprocesos? Aquí hay algunos métodos para todos.

El primer método es heredar ThreadPoolExecutor y reescribir

 protected void afterExecute(Runnable r, Throwable t) {
    
     }

con

protected void terminated() {
    
     }

Estos dos métodos.

Entre ellos, afterExecute se llamará después de que se ejecute la tarea Throwable t contiene posibles excepciones y errores en tiempo de ejecución. Podemos ocuparnos de ello según sea necesario.

Y se llama a terminado después de que se hayan llamado todas las tareas del grupo de subprocesos. Podemos limpiar algunos recursos en él.

El segundo método es usar UncaughtExceptionHandler.

Se proporciona un método setUncaughtExceptionHandler en la clase Thread para manejar la excepción detectada. Podemos agregarle un UncaughtExceptionHandler al crear el Thread.

Pero ExecutorService ejecuta un Runnable, ¿cómo usar ExecutorService para enviar Thread?

No tenga miedo, cuando los Ejecutores construyen el grupo de subprocesos, también podemos pasar ThreadFactory para construir un subproceso personalizado.

    public void useExceptionHandler() throws InterruptedException {
    
    
        ThreadFactory factory =
                new ExceptionThreadFactory(new MyExceptionHandler());
        ExecutorService pool =
                Executors.newFixedThreadPool(10, factory);
        Runnable runnable= ()->{
    
    
            throw new NullPointerException();
        };
        pool.execute(runnable);
        Thread.sleep(5000);
        System.out.println("finished!");
    }

    public static class ExceptionThreadFactory implements ThreadFactory {
    
    
        private static final ThreadFactory defaultFactory =
                Executors.defaultThreadFactory();
        private final Thread.UncaughtExceptionHandler handler;

        public ExceptionThreadFactory(
                Thread.UncaughtExceptionHandler handler)
        {
    
    
            this.handler = handler;
        }

        @Override
        public Thread newThread(Runnable run) {
    
    
            Thread thread = defaultFactory.newThread(run);
            thread.setUncaughtExceptionHandler(handler);
            return thread;
        }
    }

    public static class MyExceptionHandler implements Thread.UncaughtExceptionHandler {
    
    
        @Override
        public void uncaughtException(Thread t, Throwable e) {
    
    

        }
    }

El ejemplo anterior es un poco complicado, ¿hay alguna forma más sencilla?

algunos. Además de ejecutar para enviar tareas, ExecutorService también puede usar enviar para enviar tareas. La diferencia es que submit devolverá un Future para guardar los resultados de la ejecución.

    public void useFuture() throws InterruptedException {
    
    
        ExecutorService pool = Executors.newFixedThreadPool(10);
        Runnable runnable= ()->{
    
    
            throw new NullPointerException();
        };
        Future future = pool.submit(runnable);
        try {
    
    
            future.get();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } catch (ExecutionException e) {
    
    
            e.printStackTrace();
        }
        Thread.sleep(5000);
        System.out.println("finished!");
    }

Cuando llamamos a future.get () para obtener el resultado, la excepción también se encapsulará en ExecutionException, que podemos obtener directamente.

Preste atención a la limpieza cuando use ThreadLocal en el grupo de subprocesos

Sabemos que ThreadLocal es una variable local en Thread. Si usamos ThreadLocal durante la ejecución del subproceso, cuando el subproceso se recicla y realizamos otras tareas nuevamente, se leerán las variables previamente configuradas, resultando en desconocidos problema.

La forma correcta de usarlo es llamar a la operación de eliminación de ThreadLocal cada vez que el hilo finaliza su tarea.

O en el ThreadPoolExecutor personalizado, anule el método beforeExecute (Thread t, Runnable r) y agregue la operación de eliminación de ThreadLocal.

para resumir

He recopilado varios puntos de conocimiento, módulos, documentos y más preguntas de entrevistas reales de las principales empresas. Los amigos que lo necesiten pueden hacer clic en el enlace a continuación para obtenerlos de forma gratuita.

Enlace: 1103806531 Contraseña: CSDN

Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_48655626/article/details/109250330
Recomendado
Clasificación