Java usa un grupo de subprocesos para insertar datos mysql en lotes

Primero usamos el bucle for más primitivo para insertar datos:

for (int i = 0; i < 100000000; i++) {
            service.add(new LongTest().setStatus(1).
                    setName(NumberUtil.getPwdRandom(5)));
        }

A través de la operación anterior, se pueden insertar 1000 piezas de datos en la base de datos aproximadamente cada 3 segundos, lo que es demasiado lento. Cambiaremos a un grupo de subprocesos para la inserción de datos a continuación.

Definir la clase constante del grupo de subprocesos

/**
 * 线程池常量.
 */
public class ThreadPoolConstant {

    /**
     * 核心线程数量
     */
    public static final int CORE_THREAD_NUM = 10;

    /**
     * 最大线程数量
     */
    public static final int MAX_THREAD_NUM = 15;

    /**
     * 非核心线程存活时间
     */
    public static final long KEEP_ALIVE_TIME_SECONDS = 10L;

    /**
     * 任务队列长度
     */
    public static final int QUEUE_LENGTH = 20;

    /**
     * 线程超时时间
     */
    public static final long TIME_OUT = 70;

}

Cree un grupo de subprocesos para ejecutar subprocesos (hay varios tipos de grupos de subprocesos creados el mismo día, no daremos ejemplos uno por uno, solo enumeramos el más simple para demostración)

// 执行任务
        // 创建线程池(线程池大小、最大线程数、保持存活时间(线程空闲时间超过会退出线程)、时间单位)
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                ThreadPoolConstant.CORE_THREAD_NUM
                , ThreadPoolConstant.MAX_THREAD_NUM,
                ThreadPoolConstant.KEEP_ALIVE_TIME_SECONDS,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(ThreadPoolConstant.QUEUE_LENGTH),Executors.defaultThreadFactory()
                ,new ThreadPoolExecutor.CallerRunsPolicy());
        // 执行任务
        for (int i = 0; i < 100000000; i++) {
            final int index = i;
            threadPool.execute(() -> {
Thread.currentThread().getName()+"\n");
                service.add(new LongTest().setStatus(1).
                        setName(NumberUtil.getPwdRandom(5)));
            });

        }

A través del código del grupo de subprocesos anterior para insertar datos, en comparación con la inserción de datos tradicional, la eficiencia del uso del grupo de subprocesos ha aumentado en más del 90%. Por supuesto, también podemos modificar la constante del subproceso para el control del subproceso.

A continuación, explicaremos los parámetros de subproceso anteriores uno por uno:

público ThreadPoolExecutor(int corePoolSize, 
                          int maximumPoolSize, 
                          long keepAliveTime, 
                          unidad TimeUnit, 
                          BlockingQueue<Runnable> workQueue, 
                          ThreadFactory threadFactory, 
                          controlador RejectedExecutionHandler)

corePoolSize : ¿Cuántos subprocesos por segundo necesita? 
número de subprocesos = tareas/(1/costo de la tarea) =tareas*taskcout = (100~1000)*0.1 = 10~100 subprocesos. La configuración de corePoolSize debe ser superior a 10.
De acuerdo con el principio 8020, si el 80 % de las tareas por segundo son inferiores a 200, corePoolSize se puede establecer en 20.
 

MaximumPoolSize : el número máximo de subprocesos, el número máximo de subprocesos de recepción en el grupo de subprocesos actual, si se excede, los subprocesos en exceso esperarán o no se destruirán.

keepAliveTime : indica el tiempo de supervivencia del subproceso. Por ejemplo: 20 personas están configuradas en un sitio de construcción (el número máximo de subprocesos). Cuando finaliza el trabajo en el sitio de construcción, 10 personas están en tiempo de inactividad. el tiempo llega a la hora que establecemos, descartar (destruir).

unit : La unidad de tiempo de supervivencia del subproceso.

workQueue : selección de cola de trabajo (cola de bloqueo), hay tres colas de bloqueo de uso común en el grupo de subprocesos

BlockingQueue<Runnable> workQueue = null;
workQueue = new SynchronousQueue<>();//无缓冲的等待队列
workQueue = new ArrayBlockingQueue<>(5);//基于数组的先进先出队列
workQueue = new LinkedBlockingQueue<>();//基于链表的先进先出队列

SynchronousQueue es una cola de bloqueo que no almacena elementos y es adecuada para escenarios transitivos. Solo es responsable de entregar las tareas enviadas por el subproceso principal directamente al subproceso del grupo de subprocesos para su procesamiento. Es decir, la estrategia de rechazo se ejecutará si el número de tareas enviadas supera el número máximo de hilos.

La capa inferior de ArrayBlockingQueue es una cola de bloqueo limitada implementada por una matriz, porque se debe pasar el valor inicial (si se pasa el valor máximo de Integer, también es similar a ilimitado). La cola ordena los elementos según el principio de primero en entrar, primero en salir

La capa inferior de LinkedBlockingQueue es una cola de bloqueo limitada implementada por una lista enlazada.Si el valor de inicialización no se pasa al valor máximo de Integer, los elementos también se ordenan por orden de entrada.

En general, se recomienda elegir una cola limitada, porque si hay tantas tareas que los subprocesos principales no pueden manejar, todas las tareas se colocarán en la cola de trabajo. En este momento, la cantidad máxima de subprocesos no tiene sentido. Si el control no es bueno, conducirá a OOM

handler : El último es la elección de la estrategia de rechazo. JDK proporciona cuatro estrategias de rechazo
: AbortPolicy : descartar directamente nuevas tareas y lanzar excepciones. Cuando hay múltiples tareas, siempre que la tarea exceda la cantidad máxima de subprocesos más la cantidad de bloques establecido para la tarea, se lanzará una excepción y los subprocesos que no excedan la ejecución normal no se ejecutarán después de que se informe la excepción.

new ThreadPoolExecutor(
                ThreadPoolConstant.CORE_THREAD_NUM
                , ThreadPoolConstant.MAX_THREAD_NUM,
                ThreadPoolConstant.KEEP_ALIVE_TIME_SECONDS,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(ThreadPoolConstant.QUEUE_LENGTH),Executors.defaultThreadFactory()
                ,new ThreadPoolExecutor.AbortPolicy());

DiscardPolicy : descartarlo directamente sin lanzar una excepción. Si el número máximo de subprocesos más el número de bloques es solo 10, los primeros 10 subprocesos se ejecutarán normalmente y los subprocesos agregados más tarde se descartarán.

new ThreadPoolExecutor(
                ThreadPoolConstant.CORE_THREAD_NUM
                , ThreadPoolConstant.MAX_THREAD_NUM,
                ThreadPoolConstant.KEEP_ALIVE_TIME_SECONDS,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(ThreadPoolConstant.QUEUE_LENGTH),Executors.defaultThreadFactory()
                ,new ThreadPoolExecutor.DiscardPolicy());


DiscardOldestPolicy : descartar la tarea más antigua. Por lo general, es la tarea que se encuentra al frente de la cola. Siempre que se agreguen tareas nuevas, la tarea más antigua en la cola de bloqueo siempre se descartará y las tareas nuevas se agregarán a la cola de bloqueo.

new ThreadPoolExecutor(
                ThreadPoolConstant.CORE_THREAD_NUM
                , ThreadPoolConstant.MAX_THREAD_NUM,
                ThreadPoolConstant.KEEP_ALIVE_TIME_SECONDS,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(ThreadPoolConstant.QUEUE_LENGTH),Executors.defaultThreadFactory()
                ,new ThreadPoolExecutor.DiscardOldestPolicy());


CallerRunsPolicy : entregado al subproceso principal para su ejecución, las tareas redundantes se colocarán en la cola y la última tarea continuará ejecutándose. ( El sistema general usa este tipo de estrategia por defecto, lo que no causará la pérdida de subprocesos ), ¿qué quiere decir? En términos simples, si agregamos el número máximo de subprocesos es 2, el número de subprocesos principales es 2 y la longitud de la cola es 5, en este momento hemos agregado 10 subprocesos a la vez, de los cuales 2 subprocesos están esperando, y hay 5 subprocesos en la cola, ¿Qué pasa con 3 subprocesos? Estos 3 subprocesos no se descartarán, pero quien envíe el subproceso lo ejecutará (es decir, el subproceso principal se ejecutará y no se enviará al grupo de subprocesos)

new ThreadPoolExecutor(
                ThreadPoolConstant.CORE_THREAD_NUM
                , ThreadPoolConstant.MAX_THREAD_NUM,
                ThreadPoolConstant.KEEP_ALIVE_TIME_SECONDS,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(ThreadPoolConstant.QUEUE_LENGTH),Executors.defaultThreadFactory()
                ,new ThreadPoolExecutor.CallerRunsPolicy());

Explique  CallerRunsPolicy en detalle:

La política de rechazo CallerRunsPolicy es relativamente completa. Cuando se envía una nueva tarea, si el grupo de subprocesos no está cerrado y no se puede ejecutar, la tarea se entregará al subproceso que envió la tarea para su ejecución, es decir, quién envía la tarea. , quien es responsable de realizar las tareas. Esto tiene dos ventajas principales.

  • El primer punto es que las tareas enviadas recientemente no se descartarán, por lo que no habrá pérdida de negocio.
  • La segunda ventaja es que, dado que quien envía la tarea es responsable de ejecutarla, el subproceso que envía la tarea debe ser responsable de ejecutar la tarea, y ejecutar la tarea lleva mucho tiempo. Durante este período, el subproceso que envía la tarea es ocupado No habrá nuevas tareas enviadas, lo que ralentiza la velocidad de envío de tareas, lo que equivale a una retroalimentación negativa. Durante este período, los subprocesos en el grupo de subprocesos también pueden aprovechar al máximo este tiempo para ejecutar algunas tareas y liberar cierto espacio, lo que equivale a darle al grupo de subprocesos un cierto período de búfer.

4 tipos de estrategias de rechazo, para brindarle una explicación muy detallada de la dirección

Cuatro estrategias de rechazo del grupo de subprocesos de Java

Supongo que te gusta

Origin blog.csdn.net/qq_38935605/article/details/126382734
Recomendado
Clasificación