static final ThreadLocal
las variables son seguras para subprocesos.
static
hace que la variable ThreadLocal esté disponible en varias clases solo para el hilo respectivo. es una especie de decantación de variable global de las respectivas variables locales de subproceso en múltiples clases.
Podemos comprobar la seguridad de este subproceso con el siguiente ejemplo de código.
CurrentUser
- almacena la identificación del usuario actual en ThreadLocal
TestService
- Servicio simple con método: getUser()
para recuperar el usuario actual de CurrentUser.
TestThread
- esta clase se usa para crear múltiples subprocesos y establecer ID de usuario al mismo tiempo
.
public class CurrentUser
public class CurrentUser {
private static final ThreadLocal<String> CURRENT = new ThreadLocal<String>();
public static ThreadLocal<String> getCurrent() {
return CURRENT;
}
public static void setCurrent(String user) {
CURRENT.set(user);
}
}
public class TestService {
public String getUser() {
return CurrentUser.getCurrent().get();
}
}
.
import java.util.ArrayList;
import java.util.List;
public class TestThread {
public static void main(String[] args) {
List<Integer> integerList = new ArrayList<>();
//creates a List of 100 integers
for (int i = 0; i < 100; i++) {
integerList.add(i);
}
//parallel stream to test concurrent thread execution
integerList.parallelStream().forEach(intValue -> {
//All concurrent thread will set the user as "intValue"
CurrentUser.setCurrent("" + intValue);
//Thread creates a sample instance for TestService class
TestService testService = new TestService();
//Print the respective thread name along with "intValue" value and current user.
System.out.println("Start-"+Thread.currentThread().getName()+"->"+intValue + "->" + testService.getUser());
try {
//all concurrent thread will wait for 3 seconds
Thread.sleep(3000l);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Print the respective thread name along with "intValue" value and current user.
System.out.println("End-"+Thread.currentThread().getName()+"->"+intValue + "->" + testService.getUser());
});
}
}
.
Ejecute la clase principal TestThread. Salida -
Start-main->62->62
Start-ForkJoinPool.commonPool-worker-2->31->31
Start-ForkJoinPool.commonPool-worker-3->81->81
Start-ForkJoinPool.commonPool-worker-1->87->87
End-main->62->62
End-ForkJoinPool.commonPool-worker-1->87->87
End-ForkJoinPool.commonPool-worker-2->31->31
End-ForkJoinPool.commonPool-worker-3->81->81
Start-ForkJoinPool.commonPool-worker-2->32->32
Start-ForkJoinPool.commonPool-worker-3->82->82
Start-ForkJoinPool.commonPool-worker-1->88->88
Start-main->63->63
End-ForkJoinPool.commonPool-worker-1->88->88
End-main->63->63
...
Resumen de análisis
- El subproceso "principal" se inicia y configura el usuario actual como "62", paralelamente se inicia el subproceso "ForkJoinPool.commonPool-worker-2" y configura el usuario actual como "31", paralelamente se inicia el subproceso "ForkJoinPool.commonPool-worker-3" y configura el actual usuario como "81", paralelamente se inicia el hilo "ForkJoinPool.commonPool-worker-1" y establece el usuario actual como "87" Start-main-> 62-> 62 Start-ForkJoinPool.commonPool-worker-2-> 31-> 31 Start-ForkJoinPool.commonPool-worker-3-> 81-> 81 Start-ForkJoinPool.commonPool-worker-1-> 87-> 87
- Todos estos hilos anteriores dormirán durante 3 segundos
main
la ejecución finaliza e imprime el usuario actual como "62", la ForkJoinPool.commonPool-worker-1
ejecución paralela finaliza e imprime el usuario actual como "87", la ForkJoinPool.commonPool-worker-2
ejecución paralela termina e imprime el usuario actual como "31", la ForkJoinPool.commonPool-worker-3
ejecución paralela termina e imprime el usuario actual como "81"
Inferencia
Los subprocesos concurrentes pueden recuperar los ID de usuario correctos incluso si se han declarado como "ThreadLocal final estático"