Explicación detallada del grupo de subprocesos y la resolución de problemas de cabello.

Este artículo ha sido incluido en la columna
"Java"

nota mental

¿Qué es un grupo de subprocesos?

  Un grupo de subprocesos es un mecanismo para administrar y reutilizar subprocesos. Consiste en una cola de subprocesos y un conjunto de métodos para administrar subprocesos. Los subprocesos en el grupo de subprocesos se pueden reutilizar para ejecutar tareas enviadas sin crear y destruir subprocesos cada vez.

Componentes del grupo de subprocesos

  Cola de subprocesos: el grupo de subprocesos mantiene una cola de subprocesos para almacenar las tareas que se ejecutarán. Cuando se envía una tarea al grupo de subprocesos, el grupo de subprocesos tomará un subproceso inactivo de la cola para ejecutar la tarea.
  Administrador de subprocesos: el administrador de subprocesos del grupo de subprocesos es responsable de crear, iniciar y detener subprocesos. Crea dinámicamente subprocesos según sea necesario y los agrega a la cola de subprocesos. Cuando el grupo de subprocesos ya no necesita un subproceso, el administrador de subprocesos detiene el subproceso y lo elimina de la cola de subprocesos.
  Interfaz de tareas: la interfaz de tareas en el grupo de subprocesos define el tipo de tarea que se ejecutará. Una tarea puede ser una tarea normal que implemente la interfaz Runnable o una tarea que implemente la interfaz Callable con un valor de retorno.
  Cola de tareas: la cola de tareas del grupo de subprocesos se utiliza para almacenar las tareas que se ejecutarán. Cuando se envía una tarea al grupo de subprocesos, el grupo de subprocesos agregará la tarea a la cola de tareas y esperará a que se ejecute el subproceso.
inserte la descripción de la imagen aquí

Ventajas y desventajas

Ventajas del grupo de subprocesos

  • Reducir el consumo de recursos: el grupo de subprocesos puede reutilizar los subprocesos creados, lo que evita la sobrecarga de la creación y destrucción frecuente de subprocesos. La creación y destrucción de subprocesos necesita consumir recursos del sistema, incluidas la CPU y la memoria. Al usar el grupo de subprocesos, la creación y destrucción de subprocesos se puede concentrar en un solo lugar, lo que reduce el consumo de recursos y mejora el rendimiento y la eficiencia del sistema.
  • Mejorar la velocidad de respuesta: el grupo de subprocesos puede mejorar la velocidad de respuesta de las tareas. Los subprocesos en el grupo de subprocesos se crean previamente. Cuando llega una tarea, se puede asignar un subproceso inactivo inmediatamente para ejecutar la tarea sin esperar a que se cree e inicie el subproceso. Esto puede reducir el tiempo de espera de la tarea y mejorar la velocidad de respuesta de la tarea.
  • Controle la cantidad de subprocesos simultáneos: el grupo de subprocesos puede controlar la cantidad de subprocesos simultáneos, evitando la competencia de recursos y la degradación del rendimiento causada por demasiados subprocesos en el sistema. Al establecer el tamaño del grupo de subprocesos, la cantidad de subprocesos que se ejecutan simultáneamente se puede limitar para evitar la sobrecarga del sistema. Al mismo tiempo, el grupo de subprocesos también puede ajustar dinámicamente la cantidad de subprocesos según la carga del sistema para mantener la estabilidad y el rendimiento del sistema.
  • Proporcione funciones de administración y monitoreo de subprocesos: el grupo de subprocesos proporciona algunos métodos para administrar y monitorear subprocesos, que pueden controlar y optimizar mejor la ejecución de los subprocesos. Por ejemplo, se pueden establecer atributos como la prioridad del subproceso, el tiempo de espera y el nombre del subproceso. Al mismo tiempo, el grupo de subprocesos también proporciona algunos métodos de monitoreo, que pueden obtener información como el estado y el estado de ejecución de los subprocesos en el grupo de subprocesos, para facilitar la depuración y optimización de subprocesos.

Desventajas del grupo de subprocesos

  • Ocupación de recursos: el grupo de subprocesos creará previamente una cierta cantidad de subprocesos, y estos subprocesos siempre existirán, incluso si no hay tareas para ejecutar. Esto ocupará ciertos recursos del sistema, como la memoria y los recursos de la CPU.
    Ejemplo: supongamos que un grupo de subprocesos está configurado con 10 subprocesos, pero solo se deben ejecutar 2 tareas dentro de un período de tiempo determinado, y los otros 8 subprocesos siempre estarán inactivos y ocuparán recursos.
  • Fuga de subprocesos: los subprocesos en el grupo de subprocesos se pueden reutilizar. Cuando se ejecuta una tarea, el subproceso volverá al grupo de subprocesos y se marcará como disponible. Sin embargo, si se produce una excepción durante la ejecución de la tarea y el subproceso no se devuelve correctamente al grupo de subprocesos, se producirá una fuga de subprocesos.
    Ejemplo: se produjo una excepción durante la ejecución de una tarea y el subproceso no se devolvió al grupo de subprocesos. De esta forma, el hilo no se puede reutilizar y siempre ocupará recursos del sistema.
  • Problema de bloqueo: los subprocesos en el grupo de subprocesos son limitados. Cuando hay demasiadas tareas, es posible que el grupo de subprocesos no pueda procesar las tareas a tiempo debido a la insuficiencia de subprocesos, lo que resulta en el bloqueo de tareas.
    Ejemplo: solo hay 5 subprocesos en el grupo de subprocesos, pero es necesario ejecutar 10 tareas al mismo tiempo. Las primeras 5 tareas se pueden ejecutar de inmediato, pero las siguientes 5 tareas deben esperar a que se complete la ejecución de las tareas anteriores antes de que se puedan ejecutar, lo que hace que las tareas se bloqueen.

solicitud

Crear un grupo de subprocesos

6 formas de crear un grupo de subprocesos
inserte la descripción de la imagen aquí

Crear parámetros de grupo de subprocesos
inserte la descripción de la imagen aquí

enviar tarea

Dos formas de enviar tareas

1. ejecutor: enviar una tarea ejecutable es enviar una tarea sin valor de retorno al grupo de subprocesos

Clase ejecutiva específica

public class TaskToRunnable  implements Runnable{
    
    

    @Override
    public void run() {
    
    
        //打印线程的名字
        System.out.println("线程名:"+Thread.currentThread().getName());
    }
}

cliente

public class Client {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        //创建任务无返回值
        TaskToRunnable taskToRunnable=new TaskToRunnable();
        //创建单个线程的线程池
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        //提交任务
        threadPool.execute(taskToRunnable);
        //关闭线程池
        threadPool.shutdown();
    }
}

resultado de la operación
inserte la descripción de la imagen aquí

2. enviar: envíe una tarea invocable, que es enviar una tarea con un valor de retorno al grupo de subprocesos

Clase ejecutiva específica

public class TaskToCallable implements Callable<Integer> {
    
    
    @Override
    public Integer call() {
    
    
        return 5+5;
    }
}

cliente

public class Client {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    

        //创建任务有返回值
        TaskToCallable taskToCallable=new TaskToCallable();
        //创建单个线程的线程池
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        //提交任务
        Future<Integer> submit = threadPool.submit(taskToCallable);
        try{
    
    
            System.out.println("返回值为:"+submit.get());
        }catch (Exception e){
    
    
            e.printStackTrace();
        }finally {
    
    
            //关闭线程池
            threadPool.shutdown();
        }
    }
}

resultado de la operación
inserte la descripción de la imagen aquí

cancelar tarea

La cancelación de tareas se divide en tres fases: cancelación de tareas no ejecutadas, cancelación de tareas en ejecución y cancelación de tareas completadas, a continuación le mostraremos sus efectos en forma de código

Cancelar tareas no ejecutadas

codigo del cliente

/**
 * @BelongsProject: demo
 * @BelongsPackage: com.example.threadpool.cancel
 * @Author: Wuzilong
 * @Description: 取消未执行的任务
 * @CreateTime: 2023-07-23 23:42
 * @Version: 1.0
 */

public class UnExecutedClient {
    
    
    public static void main(String[] args) {
    
    
        TaskToCallable taskToCallable=new TaskToCallable();
        ExecutorService threadExecutor= Executors.newSingleThreadExecutor();
        Future<Integer> future1 = threadExecutor.submit(taskToCallable);
        Future<Integer> future2 = threadExecutor.submit(taskToCallable);
        boolean cancel = future2.cancel(false);
        System.out.println("任务2是否取消成功"+cancel);
        try{
    
    
            Integer integer = future2.get();
            System.out.println("任务2的返回值"+integer);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }finally {
    
    
            threadExecutor.shutdown();
        }
    }
}

Los tres ejemplos del código comercial son los mismos, los siguientes dos ejemplos ya no se escriben.

/**
 * @BelongsProject: demo
 * @BelongsPackage: com.example.threadpool.commit
 * @Author: Wuzilong
 * @Description: 业务代码
 * @CreateTime: 2023-07-23 22:58
 * @Version: 1.0
 */

public class ExecuteClientTask implements Callable<Integer> {
    
    
    @Override
    public Integer call() throws Exception {
    
    
        int i=0;
        //线程没有被中断递增i
        while(!Thread.interrupted()){
    
    
            i++;
        }
        System.out.println("当前i的值为:"+i);
        return 5+5;
    }
}

resultado de la operación
inserte la descripción de la imagen aquí

  Creamos un grupo de subprocesos con un solo subproceso y creamos dos subprocesos. Tal subproceso está en la cola de subprocesos y un subproceso está en la cola de tareas. Cancelamos el subproceso en la cola de tareas y obtenemos el valor del subproceso cancelado. Encontrará que se informará un error al obtener el valor, lo que indica que el hilo ha sido cancelado.

Cancelar una tarea en curso

codigo del cliente


/**
 * @BelongsProject: demo
 * @BelongsPackage: com.example.threadpool.cancel
 * @Author: Wuzilong
 * @Description: 取消执行中的任务
 * @CreateTime: 2023-07-23 23:50
 * @Version: 1.0
 */

public class ExecuteClient {
    
    
    public static void main(String[] args) {
    
    
        ExecuteClientTask taskToCallable=new ExecuteClientTask();
        ExecutorService threadExecutor= Executors.newSingleThreadExecutor();
        Future<Integer> future = threadExecutor.submit(taskToCallable);
        System.out.println("任务是否完成:"+future.isDone());
        //参数为false执行结果显示取消成功但是程序没有结束    任务继续执行完
        //参数为true直接结果显示取消成功程序结束了
        boolean cancel = future.cancel(true);
        System.out.println("任务1是否取消成功"+cancel);
        try{
    
    
            Integer integer = future.get();
            System.out.println("获取任务1的结果:"+integer);

        }catch (Exception e){
    
    
            e.printStackTrace();
        }finally {
    
    
            threadExecutor.shutdown();
        }
    }
}

Como resultado de la operación,
inserte la descripción de la imagen aquí
  también creamos un grupo de subprocesos que sirve como un subproceso. Después de enviar la tarea, la operación de cancelación se realiza directamente. Al realizar la operación de cancelación, pasaremos un parámetro, verdadero o falso: el El parámetro es falso, y el resultado de la ejecución muestra que la cancelación es exitosa pero el programa Si no ha terminado, la tarea continúa ejecutándose. El parámetro es verdadero y el resultado directo muestra que la cancelación se realizó correctamente y el programa terminó. En el ejemplo anterior, se pasa verdadero, y el valor no se puede obtener a continuación, y el error de cancelación del subproceso se informará directamente.

Cancelar una tarea completada

codigo del cliente

/**
 * @BelongsProject: demo
 * @BelongsPackage: com.example.threadpool.cancel
 * @Author: Wuzilong
 * @Description: 取消已完成的任务  无法取消
 * @CreateTime: 2023-07-23 23:54
 * @Version: 1.0
 */

public class ExecutedClient {
    
    
    public static void main(String[] args) {
    
    
        TaskToCallable taskToCallable=new TaskToCallable();
        ExecutorService threadExecutor= Executors.newSingleThreadExecutor();
        Future<Integer> future = threadExecutor.submit(taskToCallable);

        try{
    
    
            Integer integer = future.get();
            System.out.println("获取任务1的结果:"+integer);
            //参数只对运行时的任务有效
            boolean cancel = future.cancel(false);
            System.out.println("任务1是否取消成功"+cancel);
            System.out.println("任务1的返回值"+integer);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }finally {
    
    
            threadExecutor.shutdown();
        }
    }
}

Como resultado de la operación,
inserte la descripción de la imagen aquí
  también creamos un grupo de subprocesos como un subproceso. Después de enviar la tarea, obtuvimos el resultado de retorno de la tarea y luego cancelamos la tarea. Se encuentra que el valor de retorno de la tarea aún se puede obtener después de la cancelación, por lo que la tarea que se ha ejecutado no se puede cancelar.

inserte la descripción de la imagen aquí

rechazar tarea

Lógica de negocios

public class Task implements Runnable{
    
    
    private final int index;

    public Task(int index){
    
    
        this.index=index;
    }

    @Override
    public void run() {
    
    
        System.out.println("输出线程名:"+Thread.currentThread().getName()+"线程编号:"+index);
    }
}

cliente

/**
 * @BelongsProject: demo
 * @BelongsPackage: com.example.threadpool.refuse
 * @Author: Wuzilong
 * @Description: 线程池拒绝任务
 * @CreateTime: 2023-07-24 01:08
 * @Version: 1.0
 */

public class Client {
    
    
    public static void main(String[] args) {
    
    
        //直接丢弃,只有前两个线程
//        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1),new ThreadPoolExecutor.DiscardPolicy());
        //拒绝的任务交个调用者的线程去执行
//        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1),new ThreadPoolExecutor.CallerRunsPolicy());
        //默认的拒绝策略,直接抛出异常信息
//        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1),new ThreadPoolExecutor.AbortPolicy());
        //丢弃队列头部的任务,添加新的任务
        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(2),new ThreadPoolExecutor.DiscardOldestPolicy());

        try{
    
    
            threadPoolExecutor.execute(new Task(1));
            threadPoolExecutor.execute(new Task(2));
            threadPoolExecutor.execute(new Task(3));
            //丢弃头部的任务需要打开,前三个注释掉即可
            threadPoolExecutor.execute(new Task(4));

        }catch (Exception e){
    
    
            e.printStackTrace();
        }finally {
    
    
            //关闭线程
            threadPoolExecutor.shutdown();
        }
    }
}

Ejecución de resultados
inserte la descripción de la imagen aquí
  Ahora estamos usando la estrategia de rechazo DiscardOldestPolicy para crear un grupo de subprocesos con una cola de subprocesos y dos colas de tareas. Agregamos cuatro subprocesos al grupo de subprocesos. El resultado de la ejecución es que el segundo subproceso no se ejecuta y el cuarto subproceso es ejecutado Esto es consistente con la política de rechazo DiscardOldestPolicy que configuramos, cancelando el subproceso principal en la cola de tareas y agregando un nuevo subproceso a la cola de tareas.
inserte la descripción de la imagen aquí

cerrar operación

  Después de cerrar el grupo de subprocesos, el grupo de subprocesos no continuará recibiendo nuevas tareas y continuará ejecutando las tareas en la cola de tareas.

/**
 * @BelongsProject: demo
 * @BelongsPackage: com.example.threadpool.close
 * @Author: Wuzilong
 * @Description: 关闭线程池,不继续接收新的任务,会继续执行完任务队列中的任务
 * @CreateTime: 2023-07-24 08:54
 * @Version: 1.0
 */

public class Client {
    
    
    public static void main(String[] args) {
    
    
        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1),new ThreadPoolExecutor.AbortPolicy());
        try{
    
    
            threadPoolExecutor.execute(new Task(1));
            //将任务队列中的任务继续执行完毕
            threadPoolExecutor.execute(new Task(3));
        }catch (Exception e){
    
    
            e.printStackTrace();
        }finally {
    
    
            threadPoolExecutor.shutdown();
            //关闭线程之后继续提交任务看看是否被拒绝
            //threadPoolExecutor.execute(new Task(2));
        }
    }
}

Resultado de la ejecución
1. El resultado de continuar con la ejecución de la tarea
inserte la descripción de la imagen aquí
2. El resultado de rechazar la tarea
inserte la descripción de la imagen aquí

operación retrasada

realizar una tarea

codigo del cliente

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        Task task=new Task();
        ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(5);
        System.out.println("提交任务时间"+LocalTime.now());
        scheduledExecutorService.schedule(task,3, TimeUnit.SECONDS);
        scheduledExecutorService.shutdown();
    }
}

código comercial específico

public class Task implements Runnable{
    
    
    @Override
    public void run() {
    
    
        System.out.println("执行方法时间"+ LocalTime.now());
    }
}

Ejecución de resultados
inserte la descripción de la imagen aquí
Enviar una tarea ejecutable es lo mismo que enviar una tarea invocable. Simplemente ajuste la interfaz implementada. No hay demasiadas manifestaciones aquí. Los parámetros en el método de programación: el primero es el objeto de la tarea, el segundo es el tiempo y el tercero es la unidad de tiempo.

tareas repetitivas

Horario fijo

código comercial específico

public class Task implements Runnable{
    
    
    @Override
    public void run() {
    
    
        System.out.println("执行方法时间"+ LocalTime.now());
        try{
    
    
            //线程休眠1秒钟,模拟任务执行时长
            Thread.sleep(1000);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
    }
}

codigo del cliente

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        Task task=new Task();
        ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(5);
        System.out.println("提交任务时间"+LocalTime.now());
        scheduledExecutorService.scheduleAtFixedRate(task,1,1, TimeUnit.SECONDS);
        try{
    
    
            //当前下次线程休眠5秒
            TimeUnit.SECONDS.sleep(5);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }

        scheduledExecutorService.shutdown();
    }
}

El resultado de la ejecución
inserte la descripción de la imagen aquí
se ejecuta cada segundo. Los parámetros que se pasan en el método scheduleAtFixedRate son las tareas a ejecutar, el tiempo de retraso y el tiempo fijo para cada retraso. En el ejemplo, se pasa 1 para indicar que se ejecuta cada segundo. El más efectivo es la unidad de tiempo.

Intervalos

codigo del cliente

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        Task task=new Task();
        ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(5);
        System.out.println("提交任务时间"+LocalTime.now());
        scheduledExecutorService.scheduleWithFixedDelay(task,2,1, TimeUnit.SECONDS);
        try{
    
    
            //当前下次线程休眠5秒
            TimeUnit.SECONDS.sleep(5);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }

        scheduledExecutorService.shutdown();
    }
}

El código comercial específico es consistente con el tiempo fijo y no se repetirá aquí.
resultado de la operación
inserte la descripción de la imagen aquí

Ejecutar una vez cada dos segundos con la premisa de que se ejecuta la tarea anterior. Los parámetros pasados ​​en el método scheduleWithFixedDelay son la tarea que se ejecutará, el tiempo de retraso y el tiempo fijo para cada retraso. En el ejemplo, se pasa 2 para indicar que es anterior Partiendo de la premisa de que se ejecuta una tarea, se ejecuta cada dos segundos, y el último es la unidad de tiempo.

inserte la descripción de la imagen aquí

Comparación de tiempo fijo y tiempo de intervalo

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_45490198/article/details/131863455
Recomendado
Clasificación