Hilo local
¿La clave es nula después de GC?
incierto.
(1) Cuando se usa new ThreadLocal<>().set(s);
para definir threadlocal, si una variable no se declara en la pila para apuntar a ella, entonces solo se hace referencia débilmente a ella. Después de gc, el threadlocal se reciclará. Si la clave es nula y el valor aún está allí, causará una pérdida de memoria. Por lo tanto, asegúrese de llamar al método remove después de usar threadlocal para evitar pérdidas de memoria porque la clave es nula. .
(2) Cuando se define usando ThreadLocal<object> threadLocal = new ThreadLocal<>();
el método, es equivalente a una variable threadLocal en la pila que apunta a este objeto, formando una referencia fuerte, incluso en la entrada, como clave, es una referencia débil, pero siempre hay una variable llamado "threadLocal" en la pila. Hay una fuerte referencia que apunta a él, por lo que la clave no estará vacía en este caso. Pero mientras la variable threadLocal desaparezca después de ejecutar el método donde se encuentra la variable, dejando solo una referencia débil, la clave aún se borrará y se volverá nula.
¿Por qué la clave utiliza una referencia débil?
Si usa referencias fuertes, cuando ThreadLocal
la referencia del objeto (referencia fuerte) se recicla, ThreadLocalMap
aún conserva ThreadLocal
la referencia fuerte. Si la clave no se elimina manualmente, ThreadLocal
no se reciclará. Por lo tanto, mientras el hilo actual no muera, ThreadLocalMap
los objetos referenciados no se reciclarán, lo que se puede considerar que causa Entry
una pérdida de memoria.
Mecanismo de expansión ThreadLocalMap
Al ThreadLocalMap.set()
final del método, si no se han limpiado datos después de realizar el trabajo de limpieza heurística y el Entry
número en la matriz hash actual ha alcanzado el umbral de expansión de la lista (len*2/3)
, se iniciará la lógica de ejecución rehash()
: aquí, se realizará el trabajo de limpieza de detección. Primero se realiza table
la limpieza desde la posición inicial hacia atrás, es posible que se borren table
algunos datos , en este momento se decide si se debe ampliar la capacidad mediante key
juicio .null
Entry
size >= threshold * 3/4
Echemos un vistazo al resize()
método específico. Para facilitar la demostración, el tab
tamaño expandido es oldLen * 2
, recalcule hash
la posición y luego colóquelo en una nueva tab
matriz. Si hash
ocurre un conflicto, busque la entry
ranura más cercana null
. Una vez completado el recorrido, oldTab
todos los entry
datos se han puesto en el nuevo tab
.
Cuando se usa ThreadLocal
, los datos de copia del hilo creados en el hilo principal no se pueden compartir con el hilo secundario en un escenario asincrónico.
Para resolver este problema, también hay una clase en JDK , cuyo principio de implementación es InheritableThreadLocal
llamar a un método en el hilo principal para new Thread()
crear un hilo secundario y llamar Thread#init
a este método Thread
en el constructor. init
Copie los datos del hilo principal al hilo secundario en el método .
ThreadLocal
Uso práctico en proyectos.
- Almacenar token de usuario
- llamada de enlace traceId
ThreadPoolEjecutor
Definición de estrategia de saturación de ThreadPoolExecutor
Si la cantidad de subprocesos que se ejecutan actualmente simultáneamente alcanza la cantidad máxima de subprocesos y la cola está llena de tareas, ThreadPoolTaskExecutor
defina algunas estrategias:
ThreadPoolExecutor.AbortPolicy
: (Predeterminado) LanzarRejectedExecutionException
para rechazar el procesamiento de nuevas tareas.ThreadPoolExecutor.CallerRunsPolicy
: Llama a ejecutar su propio hilo para ejecutar la tarea, es decir,execute
ejecuta (run
) la tarea rechazada directamente en el hilo que llamó al método, si el ejecutor ha sido cerrado, la tarea será descartada. Por lo tanto, esta estrategia reducirá la velocidad de envío de nuevas tareas y afectará el rendimiento general del programa. Puede elegir esta estrategia si su aplicación puede tolerar este retraso y necesita que se ejecuten todas las solicitudes de tareas.ThreadPoolExecutor.DiscardPolicy
: Las nuevas tareas no se procesan ni descartan directamente.ThreadPoolExecutor.DiscardOldestPolicy
: Esta política descartará la solicitud de tarea no procesada más antigua.
Dos formas de crear un grupo de subprocesos
Método 1: ThreadPoolExecutor
crear mediante constructor (recomendado).
Método 2: crear a través de Executor
la clase de herramienta del marco .Executors
FixedThreadPool
:Este método devuelve un grupo de subprocesos con un número fijo de subprocesos. El número de subprocesos en el grupo de subprocesos siempre sigue siendo el mismo. Cuando se envía una nueva tarea, si hay subprocesos inactivos en el grupo de subprocesos, se ejecutará inmediatamente. De lo contrario, la nueva tarea se almacenará temporalmente en una cola de tareas y, cuando un subproceso esté inactivo, se procesará la tarea en la cola de tareas.SingleThreadExecutor
: Este método devuelve un grupo de subprocesos con un solo subproceso. Si se envía más de una tarea al grupo de subprocesos, la tarea se guardará en una cola de tareas. Cuando el subproceso esté inactivo, las tareas en la cola se ejecutarán en orden de primero en entrar, primero en salir.CachedThreadPool
: Este método devuelve un grupo de subprocesos que puede ajustar la cantidad de subprocesos según la situación real. La cantidad de subprocesos en el grupo de subprocesos es incierta, pero si hay subprocesos inactivos que se pueden reutilizar, los subprocesos reutilizables se usarán primero. Si todos los hilos están funcionando y se envía una nueva tarea, se creará un nuevo hilo para manejar la tarea. Una vez que todos los subprocesos completen la ejecución de la tarea actual, se devolverán al grupo de subprocesos para su reutilización.ScheduledThreadPool
: Esto devuelve un grupo de subprocesos utilizado para ejecutar tareas después de un retraso determinado o ejecutar tareas periódicamente.
No se recomienda el método 2 porque:
FixedThreadPool
YSingleThreadExecutor
: el uso es ilimitadoLinkedBlockingQueue
, la longitud máxima de la cola de tareas es y se puede acumular una gran cantidad de solicitudes, lo que resulta en OOMInteger.MAX_VALUE
.
CachedThreadPool
: Se utiliza una cola de sincronizaciónSynchronousQueue
, que no tiene capacidad. Cuando no hay subprocesos inactivos, los subprocesos se solicitan inmediatamente. La cantidad de subprocesos permitidos para crear esInteger.MAX_VALUE
. Se puede crear una gran cantidad de subprocesos, lo que resulta en OOM.
ScheduledThreadPool
YSingleThreadScheduledExecutor
: La cola de bloqueo de retraso ilimitado utilizadaDelayedWorkQueue
se expandirá automáticamente a la mitad de la capacidad original cuando los elementos agregados estén llenos, es decir, nunca se bloqueará. La longitud máxima de la cola de tareas es, y una gran cantidad de solicitudesInteger.MAX_VALUE
pueden se acumulan, lo que resulta en OOM.
DelayedWorkQueue
Los elementos internos no se clasifican según el tiempo de ejecución, sino que las tareas se clasifican según la duración del retraso y se utiliza la estructura de datos interna "montón" .
Análisis del principio del grupo de subprocesos.
Ejecutable vs invocable
Runnable
Las interfaces no devuelven resultados ni arrojan excepciones comprobadas, pero Callable
las interfaces sí.
La clase de herramienta Executors
puede convertir Runnable
objetos en Callable
objetos, que pertenecen al patrón de conversión.
Diferencias entre enviar y ejecutar
execute()
El método se utiliza para enviar tareas que no requieren un valor de retorno, por lo que es imposible determinar si el grupo de subprocesos ejecuta correctamente la tarea;
submit()
El método se utiliza para enviar tareas que requieren valores de retorno. El grupo de subprocesos devolverá un objeto FutureTaskFuture
de tipo y obtendrá el valor de retorno a través del método. El método bloqueará el subproceso actual hasta que se complete la tarea . Si se utiliza el método, si la tarea no se ha ejecutado dentro del tiempo, será arrojado .Future
get()
get()
get(long timeout,TimeUnit unit)
timeout
java.util.concurrent.TimeoutException
apagar() VS apagarAhora()
shutdown()
:Cierre el grupo de subprocesos y el estado del grupo de subprocesos cambiará aSHUTDOWN
. El grupo de subprocesos ya no acepta nuevas tareas, pero las tareas en la cola deben completarse .shutdownNow()
: Cierra el grupo de subprocesos y el estado del subproceso cambiaSTOP
. El grupo de subprocesos finalizará las tareas que se están ejecutando actualmente, dejará de procesar las tareas en cola y devolverá la Lista en espera de ser ejecutada .
isTerminate() VS isShutdown()
isShutDown
Devuelve verdadero cuando se llamashutdown()
al método .isTerminated
Cuandoshutdown()
se llama al método y se completan todas las tareas enviadas, devuelve verdadero
Comparación entre ScheduledThreadPoolExecutor y Timer
Timer
Sensible a cambios en el reloj del sistema,ScheduledThreadPoolExecutor
no;Timer
Sólo hay un hilo de ejecución, por lo que las tareas de larga duración pueden retrasar otras tareas.ScheduledThreadPoolExecutor
Se puede configurar cualquier número de subprocesos.ThreadFactory
Además, puedes tener control total sobre los hilos creados si lo deseas (proporcionando ;
.ScheduledThreadPoolExecutor
Se puede configurar cualquier número de subprocesos.ThreadFactory
Además, puedes tener control total sobre los hilos creados si lo deseas (proporcionando ;TimerTask
Una excepción de tiempo de ejecución lanzada matará un subproceso, lo que provocará pánicoTimer
, es decir, la tarea programada ya no se ejecutará.ScheduledThreadExecutor
No solo detecta excepciones en tiempo de ejecución, sino que también le permite manejarlas si es necesario (anulandoafterExecute
el métodoThreadPoolExecutor
). La tarea que genera la excepción se cancelará, pero otras tareas seguirán ejecutándose.