Diagrama de estado de hilo
Los hilos incluyen los siguientes 5 estados.
1. Nuevo estado: después de que se crea el objeto de subproceso, ingresa al nuevo estado. Por ejemplo, Thread Thread = New Thread ().
2. Estado listo (Runnable): también conocido como "Estado ejecutable". Después de que se crea el objeto de subproceso, otros hilos llaman al método Start () del objeto para iniciar el hilo. Por ejemplo, Thread.Start (). Se puede programar un hilo en un estado listo para ejecutar por la CPU en cualquier momento.
3. Estado en ejecución (en ejecución): el hilo obtiene permisos de CPU para la ejecución. Cabe señalar que los hilos solo pueden ingresar al estado en ejecución desde el estado listo.
4. Estado bloqueado: Estado bloqueado significa que el hilo renuncia a los derechos de uso de la CPU por alguna razón y deja de funcionar temporalmente. No es hasta que el hilo ingresa al estado listo que tiene la oportunidad de ir al estado en funcionamiento. Hay tres tipos de bloqueo:
(01) Esperando bloquear: llamando al método Wait () del hilo, deje que el hilo espere la finalización de un cierto trabajo.
(02) Bloqueo sincronizado: un hilo no puede adquirir un bloqueo de sincronización sincronizado (debido a que el bloqueo está ocupado por otros hilos), ingresará a un estado de bloqueo sincronizado.
(03) Otro bloqueo: el hilo ingresará a un estado de bloqueo llamando a dormir () o unirse () del hilo o emitir una solicitud de E/S. Cuando el estado de sueño () se agotó, unirse () esperó a que el hilo termine o se agotara, o se completó el procesamiento de E/S, el hilo volvió a ingresar al estado listo.
5. Estado muerto: el hilo ha terminado de ejecutar o salir del método run () debido a una excepción, y el hilo finaliza su ciclo de vida.
Implementar hilo de métodos de múltiples subprocesos y ejecutables
Thread: herede la clase de subproceso, implementa el método Ejecutar y llame al método de inicio en la función principal para iniciar el hilo
Runnable: interfaz, implementa la interfaz runnable, la pasa como un parámetro para el constructor de subprocesos y llama al método de inicio en main
ejemplo:
implementos de tarea de clase runnable {private int ticket = 10; @Override public void run () {for (int i = 0; i <20; i ++) {if (this.ticket> 0) {system.out.println (thread.currentThread (). GetName () + "ticket vendido" + this.ticket--); }}}}}}; public class runnabletest {public static void main (string [] args) {tarea myTask = new Task (); Hilo t1 = nuevo hilo (mytask); Hilo t2 = nuevo hilo (mytask); Hilo t3 = nuevo hilo (myTask); t1.start (); t2.start (); t3.start (); }} // threadtest.java El código de origen clase myThread extiende hilo {private int ticket = 10; public void run () {for (int i = 0; i <20; i ++) {if (this.ticket> 0) {system.out.println (this.getName () + "ticket sell: ticket" + this.ticket--); }}}} public class ThreadTest {public static void main (string [] args) {// iniciar 3 hilos t1, t2, t3; ¡Cada hilo vende 10 boletos cada uno! Mythread t1 = new MyThread (); MyThread t2 = new MyThread (); Mythread t3 = new MyThread (); t1.start (); t2.start (); t3.start (); }};
La diferencia entre hilo y runnable
Thread es una clase, y Runnable es una interfaz; Thread en sí es una clase que implementa la interfaz ejecutable. Sabemos que "una clase solo puede tener una clase principal, pero puede implementar múltiples interfaces", por lo que Runnable tiene una mejor escalabilidad. Además, Runnable también se puede utilizar para "compartir recursos". Es decir, se crean múltiples hilos basados en un cierto objeto ejecutable, y compartirán recursos en el objeto ejecutable. ¡En general, se recomienda implementar múltiples subprocesos a través de "Runnable"!
Ejecute y comienza el hilo
Start (): su función es iniciar un nuevo hilo, y el nuevo hilo ejecutará el método ejecutivo () correspondiente. inicio () no se puede llamar repetidamente. Start () realmente inicia el hilo a través del método local Start0 (). start0 () ejecutará un nuevo hilo, y el nuevo hilo llamará al método run ().
run (): run () se puede llamar repetidamente al igual que los métodos de miembros ordinarios. Si llama a ejecutar () por separado, run () se ejecutará en el hilo actual, ¡y el nuevo hilo no se iniciará! run () es para llamar directamente al método run () del miembro ejecutable del hilo del subproceso, y no creará un nuevo hilo.
// La clase de código fuente de Java MyThread extiende el hilo {public mythread (name de cadena) {super (nombre); } public void run () {System.out.println (Thread.CurrentThread (). getName ()+"está ejecutando"); }}; Public Class Demo {public static void main (string [] args) {Thread myThread = new MyThread ("MyThread"); System.out.println (thread.currentThread (). GetName ()+"llamar mythread.run ()"); mythread.run (); System.out.println (thread.currentThread (). GetName ()+"llamar mythread.start ()"); mythread.start (); }}Producción:
llamada principal mythread.run () main is runningmain llame mythread.start () mythread se está ejecutando
sincronizado
En Java, cada objeto tiene un bloqueo de sincronización. Cuando llamamos al método sincronizado del objeto, el bloqueo del objeto se adquiere y se sincroniza (OBJ) adquiere el bloqueo de sincronización del "objeto OBJ". El acceso de diferentes hilos al bloqueo de sincronización es mutuamente excluyente. El bloqueo de sincronización del objeto solo se puede adquirir mediante un hilo en un momento determinado. A través del bloqueo de sincronización, podemos lograr un acceso mutuamente excluyente al "objeto/método" en múltiples hilos. Por ejemplo, ahora hay dos hilos A y Hilo B, que accederán al "bloqueo sincronizado de Obj OBJ". Supongamos que en algún momento, el enhebrado A adquiere el "bloqueo de sincronización de OBJ" y realiza algunas operaciones; En este momento, el hilo B también intenta adquirir el "bloqueo de sincronización de OBJ": el hilo B no puede adquirir, debe esperar hasta que el hilo A libere el "bloqueo de sincronización de OBJ" y solo se puede ejecutar.
Reglas básicas
Artículo 1: Cuando un hilo accede al "método sincronizado" o "bloque de código sincronizado" de "un cierto objeto", otros subprocesos se bloquearán desde el acceso al "método sincronizado" o "bloque de código sincronizado" de "el objeto".
Artículo 2: Cuando un hilo accede al "método sincronizado" o "bloque de código sincronizado" de "un cierto objeto", otros subprocesos aún pueden acceder al bloque de código asincronizado de "este objeto".
Artículo 3: Cuando un hilo accede al "método sincronizado" o "bloque de código sincronizado" de "un cierto objeto", otros subprocesos se bloquearán para acceder a otros "métodos sincronizados" o "bloque de código sincronizado" de "el objeto".
método sincronizado
public sincronizado void foo1 () {system.out.println ("método sincronizado");} bloque de código sincronizado public void foo2 () {sincronizado (this) {system.out.println ("método sincronizado"); }}Esto en el bloque de código sincronizado se refiere al objeto actual. Esto también se puede reemplazar con otros objetos, como este se reemplaza con OBJ, luego Foo2 () adquiere el bloqueo de sincronización de OBJ cuando se sincroniza (OBJ).
Los bloques de código sincronizados pueden controlar las áreas de acceso restringidas al conflicto con mayor precisión y, a veces, funcionar con más eficiencia
Bloqueo de instancia y bloqueo global
Bloqueo de instancia: bloqueado en un objeto de instancia. Si la clase es un singleton, entonces el bloqueo también tiene el concepto de un bloqueo global. La palabra clave sincronizada corresponde al bloqueo de instancia.
Lock Global: esta cerradura está dirigida a una clase. No importa cuántos objetos sea la instancia, los hilos comparten el bloqueo. El bloqueo global corresponde a static sincronizado (o bloqueado en la clase o objeto de cargador de clase de esta clase).
Clase pulbica algo {public sincronizado Void isSynca () {} public sincronizado Void isSyncb () {} public static sincronizado void csynca () {} public sincronizado void sincronizado sincronizado csyncb () {}}
(01) X.ISSYNCA () y X.ISSYNCB () no se pueden acceder simultáneamente. ¡Porque isSynca () e isSyncb () son bloqueos de sincronización que acceden al mismo objeto (objeto X)!
(02) X.issynca () y Y.issynca () se puede acceder al mismo tiempo. Debido a que no está accediendo al bloqueo de sincronización del mismo objeto, x.issynca () accede al bloqueo de sincronización de x, mientras que Y.issynca () accede al bloqueo de sincronización de y.
(03) X.Csynca () y Y.Csyncb () no se pueden acceder simultáneamente. Debido a que csynca () y csyncb () son tipos estáticos, x.csynca () es equivalente a algo.issynca (), y y.csyncb () es equivalente a algo.issyncb (), comparten un bloqueo de sincronización y no se pueden preguntar al mismo tiempo.
(04) x.issynca () y algo.csynca () se puede acceder simultáneamente. Debido a que isnynca () es un método de instancia, X.issynca () usa el bloqueo del objeto X; Mientras que csynca () es un método estático, algo.csynca () puede entender que es un "bloqueo de clase" utilizado. Por lo tanto, se puede acceder simultáneamente.
Bloqueo de hilos y despertar espera, notificar, notificar a todos
En Object.java, se definen interfaces como Wait (), notify () y notifyall (). La función de Wait () es dejar que el hilo actual ingrese a un estado de espera, y Wait () también permitirá que el hilo actual libera el bloqueo que contiene. El papel de notificar () y notifyall () es despertar el hilo de espera en el objeto actual; Notify () es despertar un solo hilo, mientras que Notifyall () es despertar todos los hilos.
Los detalles de la API sobre Waiting/Wake en la clase de objetos son los siguientes:
Notificar () - despertar un solo hilo esperando en este monitor de objeto.
NotifyAll () - despertar todos los hilos esperando en este monitor de objeto.
Wait (): coloque el hilo actual en un "estado de espera (bloqueo)" y "hasta que otros hilos llamen al método notify () o notifyall () de este objeto", y el hilo actual se despierta (ingresado al "estado listo").
espera (tiempo de espera largo): coloque el hilo actual en un "estado de espera (bloqueo)" y "hasta que otros hilos llamen al método del objeto notify () o notifyall (), o excedan la cantidad de tiempo especificada", y el hilo actual se despierta (ingresado al "estado listo").
Espera (tiempo de tiempo largo, int nanos): deje que el hilo actual esté en un estado de "esperar (bloquear)", "hasta que otro hilo llame al método del objeto notify () o notifyall (), o algún otro hilo interrumpe el hilo actual, o ha superado una cierta cantidad de tiempo", y el hilo actual se despierta (ingresado al "estado listo").
// La clase de código fuente de waitest.java Thinda extiende el hilo {publicada public (nombre de cadena) {super (nombre); } public void run () {SynChronized (this) {System.out.println (Thread.CurrentThread (). GetName ()+"Call notify ()"); // despertar el hilo de espera actual notify (); }}} public class waittest {public static void main (string [] args) {thinda t1 = new thinda ("t1"); sincronizado (t1) {try {// iniciar "hilo t1" system.out.println (thread.currentThread (). getName ()+"inicio t1"); t1.start (); // El hilo principal espera a que T1 se despierte a través de Notify (). System.out.println (Thread.CurrentThread (). GetName ()+"Wait ()"); t1.wait (); System.out.println (thread.currentThread (). GetName ()+"continuar"); } catch (InterruptedException e) {E.PrintStackTrace (); }}}}Producción
Inicio principal T1Main Wait () T1 Llama Notify () Principal continuar
(01) Tenga en cuenta que el "hilo principal" en la figura representa el "hilo principal principal". "Hilo T1" representa "Hilo T1" comenzó en Waittest. Y "bloquear" representa "el bloqueo sincrónico del objeto T1".
(02) "El hilo principal" crea un nuevo "hilo T1" a través de un nuevo thrave ("T1"). Luego, el "bloqueo sincrónico del objeto T1" se obtiene a través de sincronizado (T1). Luego llame a T1.Start () para comenzar "Hilo T1".
(03) "El hilo principal" ejecuta t1.wait () para liberar "bloqueo del objeto T1" e ingresa "estado de espera (bloqueo)". Espere los hilos en los objetos T1 para despertarlo a través de notificar () o notifyall ().
(04) Después de que se ejecuta "Hilo T1", el "bloqueo del objeto actual" se obtiene a través de sincronizado (esto); Luego llame a Notify () para despertar el "hilo de espera en el objeto actual", es decir, despierta el "hilo principal".
(05) Después de que se complete "Subpuste T1", suelte el "Bloqueo del objeto actual". Inmediatamente después, el "hilo principal" adquiere el "bloqueo del objeto T1" y luego se ejecuta.
T1.Wait () es el método Wait () llamado a través de "Hilo T1", pero el lugar donde se llama T1.Wait () está en "PRINCIPAL MAIN". El hilo principal debe ser el "hilo actual", es decir, el estado en ejecución, antes de que se pueda ejecutar t1.wait (). ¡Por lo tanto, el "hilo actual" en este momento es el "hilo principal principal"! Por lo tanto, t1.wait () es hacer el "hilo principal" esperar, no "hilo T1"!
paquete Thread.test; clase pública notifyAllTest {objeto estático privado obj = nuevo objeto (); public static void main (string [] args) {thinda t1 = new thinda ("t1"); Thinta t2 = new thinda ("t2"); Thinta t3 = new thinda ("t3"); t1.start (); t2.start (); t3.start (); Pruebe {System.out.println (Thread.CurrentThread (). GetName ()+"Sleep (3000)"); Thread.sleep (3000); } catch (InterruptedException e) {E.PrintStackTrace (); } sincronizado (obj) {system.out.println (thread.currentThread (). getName ()+"notifyAll ()"); obj.notifyAll (); // wake t1.t2.t3 aquí}} clase static thadsa extiende el hilo {publicada public (nombre de cadena) {super (nombre); } public void run () {SynChronized (obj) {try {// impresión de impresión System.out.println (Thread.CurrentThread (). GetName () + "Wait"); // libera el bloqueo obj obj.wait (); // Impresión de resultados System.out.println (Thread.CurrentThread (). GetName () + "Continuar"); } catch (InterruptedException e) {E.PrintStackTrace (); }}}}}} Producción:
T1 Waitmain Sleep (3000) T3 Waitt2 Waitmain notifyall () T2 Contint3 Continuar1 Continuar
(01) 3 hilos "T1", "T2" y "T3" fueron creados y comenzaron en el hilo principal.
(02) El hilo principal duerme durante 3 segundos durante el sueño (3000). Durante el sueño principal del hilo durante 3 segundos, suponemos que los tres hilos "T1", "T2" y "T3" están funcionando. Tome "T1" como ejemplo. Cuando se ejecute, ejecutará obj.wait () para esperar a que otros hilos se despierten a través de notificar () o nofityall (); Por el mismo token, "T2" y "T3" también esperarán a que otros hilos los despierten a través de nofity () o nofityall ().
(03) El hilo principal duerme durante 3 segundos y luego corre. Ejecute obj.notifyall () para despertar el hilo de espera en OBJ, es decir, despierta los tres hilos "T1", "T2" y "T3". Inmediatamente después de que se ejecuta el hilo principal (OBJ), el hilo principal libera el "bloqueo OBJ". De esta manera, "T1", "T2" y "T3" pueden obtener el "Bloqueo OBJ" y continuar ejecutándose.
notificar, notificar a todos y bloquear la relación
Funciones como Wait (), notificar () en objeto, como sincronizado, funcionarán en "Bloqueo de sincronización de objetos".
Wait () hará que el "hilo actual" espera. Debido a que el hilo ingresa al estado de espera, el hilo debe liberar el "bloqueo sincrónico" sostenido por su bloqueo, de lo contrario, otros hilos no podrán obtener el "bloqueo sincrónico" y no podrán ejecutar.
Ok, después de que el hilo llame a Wait (), lanzará el "bloqueo sincrónico" sostenido por su bloqueo; y, según la introducción anterior, sabemos que el hilo de espera puede despertarse por notify () o notifyall (). Ahora, piense en una pregunta: ¿Qué es notificar () basado en despertar el hilo de espera? ¿O cuál es la correlación entre Wait () y notificar ()? La respuesta es: basada en "bloqueo de sincronización de objetos".
El hilo responsable de despertar el hilo de espera (lo llamamos "hilo de despertar"), solo puede despertar el hilo de espera después de obtener el "bloqueo sincrónico de este objeto" (el bloqueo de sincronización aquí debe ser lo mismo que el bloqueo de sincronización del hilo de espera) y llamar a los métodos notify () o notifyall (). Aunque, el hilo de espera se despierta; Sin embargo, no se puede ejecutar de inmediato porque el hilo de despertar todavía contiene "bloqueo sincrónico para el objeto". Debe esperar hasta que el hilo de activación libere el "bloqueo de sincronización del objeto" antes de poder obtener el "bloqueo de sincronización del objeto" y continuar ejecutándose.
En resumen, notify (), Wait () se basa en "Lock Syncronous", que se mantiene en bloqueos de objetos, y cada objeto tiene y solo uno! Es por eso que funciones como notify (), wait () se definen en la clase de objeto, no en la clase de hilo.
Rendimiento de concesiones de hilo
Las concesiones de hilo hacen que el hilo cambie del estado de ejecución al estado listo, para que otros hilos de espera con la misma prioridad puedan obtener los derechos de ejecución; Sin embargo, no se garantiza que después de la actualización actual de las llamadas al rendimiento (), otros hilos con la misma prioridad definitivamente obtendrán derechos de ejecución; También es posible que el hilo actual ingrese al "estado en ejecución" y continúe ejecutándose.
rendimiento y espera
(01) Wait () es dejar que el hilo ingrese al "Estado de espera (Bloqueo)" del "Estado en ejecución", mientras que no rendimiento () es dejar que el hilo ingrese al "Estado listo" del "Estado en ejecución".
(02) Wait () es un bloqueo de sincronización que en el rostro de la liberación del objeto que posee, mientras que el método de rendimiento () no liberará el bloqueo.
(03) esperar es el método de objeto, el rendimiento es el método de hilo
Duerme de hilo
La función de Sleep () es dejar que el hilo actual duerma, es decir, el hilo actual entrará desde el "estado en ejecución" al "bloqueo (bloqueo)". Sleep () especificará el tiempo de sueño, y el tiempo de sueño del hilo será mayor que/igual al tiempo de sueño; Cuando el hilo se despierta nuevamente, cambiará de un "estado de bloqueo" a un "estado listo", esperando que la CPU sea programada para ejecutar.
La diferencia entre dormir y esperar
La función de Wait () es permitir que el hilo actual ingrese al estado "Wait (Bloqueo) desde el" Estado en ejecución "y también libere el bloqueo de sincronización. La función de Sleep () es dejar que el hilo actual ingrese al estado" Sleep (Bloqueo) desde el "Estado en ejecución". (Esto en realidad no es muy diferente)
Wait () libera el bloqueo de sincronización del objeto, mientras que Sleep () no libera el bloqueo
esperar es el método de objeto, el sueño es el método de hilo
Unirse
Deje que el hilo principal espere y el hilo infantil puede continuar funcionando después de completar el hilo principal.
interrumpir
Se usa para terminar un hilo bloqueado
@OverridePublic void run () {try {while (true) {// Ejecutar la tarea ...}} Catch (InterruptedException IE) {// debido a una excepción de InterruptedException, ¡salga el bucle while (true) y el hilo termina! }}En While (verdadero), la llamada a interrupción () del hilo genera una interrupción de interrupción. La captura interrumpida está afuera mientras (verdadero), saliendo así del bucle while (verdadero)
Termina un hilo en el estado en funcionamiento
@OverridePublic void run () {while (! Isinterrupted ()) {// ejecutar tarea ...}}Forma común de terminar hilos
@OverridePublic Void run () {try {// 1. ISInterrupted () garantiza que el hilo finalizará siempre que la interrupción esté marcada verdadera. while (! IsInterrupted ()) {// Ejecutar tarea ...}} Catch (InterruptedException IE) {// 2. La excepción de InterruptedException garantiza que cuando ocurre una excepción de InterruptedException, el hilo se termina. }}
Prioridad del hilo
El rango de prioridad de hilo en Java es de 1 a 10, y la prioridad predeterminada es 5. "Los hilos de alta prioridad" precederán a la ejecución en "subprocesos de baja prioridad". Hay dos tipos de hilos en Java: hilo de usuario y hilo de demonio. Se pueden distinguir mediante el método isdaemon (): si se devuelve el falso, significa que el hilo es un "hilo del usuario"; De lo contrario, es un "hilo de demonio". Los hilos de usuario generalmente realizan tareas a nivel de usuario, mientras que los hilos de demonio también son "hilos de backend", que generalmente se usan para realizar tareas de fondo. Cabe señalar que la máquina virtual Java saldrá después de que se complete el "hilo del usuario".
Cada hilo tiene una prioridad. Los "hilos de alta prioridad" precederán a la ejecución en "hilos de baja prioridad". Cada hilo se puede marcar como un demonio o no damon. Al crear un nuevo hilo infantil en algún hilo principal en ejecución, la prioridad del hilo infantil se establece en igual a "prioridad del hilo principal que lo creó", y "el hilo infantil será el hilo de demonio" cuando y solo si "el hilo principal que lo creó es un hilo de demonio".
Cuando se inicia una máquina virtual Java, generalmente hay un solo hilo de no dademon (este hilo se inicia a través del método Main ()). El JVM se ejecutará hasta que ocurra cualquiera de las siguientes condiciones, y el JVM terminará la ejecución:
(01) El método Exit () se llama, y Exit () tiene permiso para ejecutarse normalmente.
(02) Todos los "hilos no dadon" están muertos (es decir, solo hay "hilos de demonio" en el JVM).
Demonio
(01) El subproceso principal es el subproceso del usuario, y el hilo infantil que crea también es el hilo del usuario.
(02) T2 es el hilo de demonio. Cuando se ejecuta el "hilo principal principal" y el "sub-thread T1" (ambos son hilos de usuario) y solo queda el hilo de demonio T2, el JVM sale automáticamente.
Problemas de productor y consumidor
(01) El productor solo produce cuando el almacén no está lleno y detiene la producción cuando el almacén está lleno.
(02) Los consumidores solo pueden consumir cuando tienen productos en almacenamiento, y esperar si tienen almacenes vacíos.
(03) Cuando los consumidores encuentran que no hay un producto para consumir en el almacén, notificarán al productor.
(04) Cuando los productores producen productos consumibles, deben notificar a los consumidores que esperan que consuman.
Comunicación entre hilos
Método: memoria y mensajes compartidos
Memoria compartida: el subproceso A y el hilo B comparten la memoria, el hilo A actualiza el valor de la variable compartida, la actualiza a la memoria principal y el hilo B va a la memoria principal para leer las variables actualizadas del hilo A. Todo el proceso de comunicación debe pasar a través de la memoria principal. La sincronización se realiza explícitamente.
Si una variable es de tipo volátil, la lectura y la escritura de la variable serán atómicas. Si se trata de múltiples operaciones volátiles o operaciones compuestas similares a volátiles ++, estas operaciones no son atómicas en su totalidad.
La variable volátil en sí tiene las siguientes características:
[Visibilidad]: al leer una variable volátil, siempre puede ver la última escritura en la variable volátil (cualquier hilo).
[Atomicidad]: Tiene atomicidad para la lectura/escritura de cualquier variable volátil única, pero no tiene atomicidad para operaciones compuestas similares a volátiles ++.
Escritura volátil: al escribir una variable volátil, JMM descargará la variable compartida en la memoria local correspondiente al hilo a la memoria principal.
Lectura volátil: al leer una variable volátil, JMM invalidará la memoria local correspondiente al hilo. A continuación, el hilo leerá la variable compartida de la memoria principal.
Entrega de mensajes: el envío de un mensaje se realiza implícitamente antes de que se acepte el mensaje.
Hilo
ThreadLocal no se usa para resolver el problema del acceso múltiple a objetos compartidos. En términos generales, el objeto al hilo a través de ThreadLocal.set () es un objeto utilizado por el hilo en sí. No es necesario acceder a otros hilos y no se puede acceder. ThreadLocal permite que cada hilo mantenga su propio objeto independiente. No se implementa a través de ThreadLocal.set (), sino un objeto creado por la operación del nuevo objeto en cada hilo. Cada hilo crea uno, no una copia o copia del objeto. La referencia al objeto recién creado se guarda en el mapa propio de cada hilo a través de ThreadLocal.set (). Cada hilo tiene ese mapa. Cuando se ejecuta ThreadLocal.get (), cada hilo saca el objeto colocado desde su propio mapa. Por lo tanto, lo que se toma es el objeto en cada hilo. La instancia de ThreadLocal se usa como la tecla MAP. Si la cosa que entra ThreadLocal.set () es el mismo objeto compartido por múltiples hilos, entonces el ThreadLocal.get () de múltiples hilos todavía obtiene el objeto compartido en sí, y todavía hay un problema de acceso concurrente.
Una implementación de ThreadLocal
import java.util.collections; import java.util.hashmap; import java.util.map; /** * clase usando ThreadLocal * * @author Leizhimin 2010-1-5 10:35:27 */public class myThreadLocal {// Definir una variable de ThreadLocal para guardar int o Integer Data privado com.laVASoft.test2.threadlocal <ToReger> tl = new Com.Lavasoft.test2.threadlocal <Tineger> () Entero inicialValue () {return 0; }}; Public Integer getNextNum () {// Obtenga el valor de TL y agregue 1, y actualice el valor de t1 tl.set (tl.get () + 1); return tl.get (); }} class ThreadLocal <T> {private Map <Thread, t> map = colección.synchronizedMap (new HashMap <Thread, t> ()); public ThreadLocal () {} protegido t inicialValue () {return null; } public t get () {Thread t = Thread.CurrentThread (); T obj = map.get (t); if (obj == null &&! map.containskey (t)) {obj = inicialValue (); map.put (t, obj); } return obj; } Public void set (t value) {map.put (thread.currentThread (), valor); } public void remove () {map.remove (thread.currentThread ()); }}De hecho, ThreadLocal hace esto:
public t get () {Thread t = Thread.CurrentThread (); ThreadLocalMap map = getMap (t); if (map! = null) {threadlocalmap.entry e = map.getEntry (this); if (e! = null) return (t) e.value; } return setInitialValue (); }