Los datos compartidos son una de las características más críticas de los programas concurrentes. Este es un aspecto muy importante, ya sea un objeto que hereda la clase Thread o un objeto que implementa la interfaz Runnable.
Si crea un objeto de una clase que implementa la interfaz Runnable y usa ese objeto para iniciar una serie de subprocesos, todos esos subprocesos comparten las mismas propiedades. En otras palabras, si un subproceso modifica una propiedad, todos los subprocesos restantes se verán afectados por el cambio.
A veces, preferimos usarlo solo dentro de un hilo en lugar de compartirlo con otros hilos iniciados con el mismo objeto. La interfaz de concurrencia de Java proporciona un mecanismo muy claro para cumplir con este requisito, llamado variables locales de subproceso. El rendimiento de este mecanismo también es muy impresionante.
lo sabes
Siga los pasos que se muestran a continuación para completar el programa de muestra.
1. Primero, implemente un programa con los problemas anteriores. Cree una clase llamada UnsafeTask e implemente la interfaz Runnable. Declare una propiedad privada de tipo java.util.Date en la clase. El código es el siguiente:
Copie el código de código de la siguiente manera:
La clase pública UnsafeTask implementa Runnable {
fecha privada fecha de inicio;
2. Implemente el método run() de UnsafeTask, que crea una instancia del atributo startDate y envía su valor a la consola. Duerma durante un período de tiempo aleatorio y luego imprima nuevamente el valor del atributo startDate en la consola. El código es el siguiente:
Copie el código de código de la siguiente manera:
@Anular
ejecución pública vacía() {
fecha de inicio = nueva fecha();
System.out.printf("Hilo inicial: %s: %s/n",
Thread.currentThread().getId(), fecha de inicio);
intentar {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} captura (Excepción interrumpida e) {
e.printStackTrace();
}
System.out.printf("Subproceso finalizado: %s: %s/n",
Thread.currentThread().getId(), fecha de inicio);
}
3. Implementar la clase principal del programa problemático. Cree una clase con un método main(), UnsafeMain. En el método main(), cree un objeto UnsafeTask y use este objeto para crear 10 objetos Thread para iniciar 10 subprocesos. En medio de cada hilo, duerme 2 segundos. El código es el siguiente:
Copie el código de código de la siguiente manera:
clase pública UnsafeMain {
público estático vacío principal (String [] argumentos) {
Tarea UnsafeTask = nueva UnsafeTask();
para (int i = 0; i < 10; i++) {
Hilo hilo = nuevo hilo (tarea);
hilo.start();
intentar {
Unidad de tiempo.SEGUNDOS.dormir(2);
} captura (Excepción interrumpida e) {
e.printStackTrace();
}
}
}
}
4. Según la lógica anterior, cada hilo tiene un tiempo de inicio diferente. Sin embargo, según el registro de salida siguiente, hay muchos valores de tiempo idénticos. como sigue:
Copie el código de código de la siguiente manera:
Hilo inicial: 9: domingo 29 de septiembre 23:31:08 CST 2013
Hilo inicial: 10: domingo 29 de septiembre 23:31:10 CST 2013
Hilo inicial: 11: domingo 29 de septiembre 23:31:12 CST 2013
Hilo inicial: 12: domingo 29 de septiembre 23:31:14 CST 2013
Tema terminado: 9: domingo 29 de septiembre 23:31:14 CST 2013
Hilo inicial: 13: domingo 29 de septiembre 23:31:16 CST 2013
Tema terminado: 10: domingo 29 de septiembre 23:31:16 CST 2013
Hilo inicial: 14: domingo 29 de septiembre 23:31:18 CST 2013
Tema terminado: 11: domingo 29 de septiembre 23:31:18 CST 2013
Hilo inicial: 15: domingo 29 de septiembre 23:31:20 CST 2013
Tema terminado: 12: domingo 29 de septiembre 23:31:20 CST 2013
Hilo inicial: 16: domingo 29 de septiembre 23:31:22 CST 2013
Hilo inicial: 17: domingo 29 de septiembre 23:31:24 CST 2013
Tema terminado: 17: domingo 29 de septiembre 23:31:24 CST 2013
Tema terminado: 15: domingo 29 de septiembre 23:31:24 CST 2013
Tema terminado: 13: domingo 29 de septiembre 23:31:24 CST 2013
Hilo inicial: 18: domingo 29 de septiembre 23:31:26 CST 2013
Tema terminado: 14: domingo 29 de septiembre 23:31:26 CST 2013
Tema terminado: 18: domingo 29 de septiembre 23:31:26 CST 2013
Tema terminado: 16: domingo 29 de septiembre 23:31:26 CST 2013
5. Como se muestra arriba, utilizaremos el mecanismo de variables locales de subproceso para resolver este problema.
6. Cree una clase llamada SafeTask e implemente la interfaz Runnable. El código es el siguiente:
Copie el código de código de la siguiente manera:
La clase pública SafeTask implementa Runnable {
7. Declare un objeto de tipo ThreadLocal<Fecha>. Cuando se crea una instancia del objeto, el método inicialValue() se anula y el valor de fecha real se devuelve en este método. El código es el siguiente:
Copie el código de código de la siguiente manera:
ThreadLocal estático privado <Fecha> fecha de inicio = nuevo
ThreadLocal<Fecha>() {
@Anular
fecha protegida valor inicial() {
devolver nueva fecha();
}
};
8. Implemente el método run() de la clase SafeTask. Este método es el mismo que el método run() de UnsafeTask, excepto que el método del atributo startDate está ligeramente ajustado. El código es el siguiente:
Copie el código de código de la siguiente manera:
@Anular
ejecución pública vacía() {
System.out.printf("Hilo inicial: %s: %s/n",
Thread.currentThread().getId(), startDate.get());
intentar {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} captura (Excepción interrumpida e) {
e.printStackTrace();
}
System.out.printf("Subproceso finalizado: %s: %s/n",
Thread.currentThread().getId(), startDate.get());
}
9. La clase principal de este ejemplo seguro es básicamente la misma que la clase principal del programa no seguro, excepto que UnsafeTask debe modificarse a SafeTask. El código específico es el siguiente:
Copie el código de código de la siguiente manera:
clase pública SafeMain {
público estático vacío principal (String [] argumentos) {
Tarea SafeTask = nueva SafeTask();
para (int i = 0; i < 10; i++) {
Hilo hilo = nuevo hilo (tarea);
hilo.start();
intentar {
Unidad de tiempo.SEGUNDOS.dormir(2);
} captura (Excepción interrumpida e) {
e.printStackTrace();
}
}
}
}
10. Ejecute el programa y analice las diferencias entre las dos entradas.
Para estandarizar la denominación de las clases, la denominación de la clase principal en este artículo es ligeramente diferente del texto original. Además, el programa original y la descripción del texto son inconsistentes. Debe ser un error administrativo.
saber por qué
A continuación se muestra el resultado de la ejecución del ejemplo de seguridad. A partir de los resultados, se puede ver fácilmente que cada hilo tiene un valor de atributo startDate que pertenece al hilo respectivo. La entrada del programa es la siguiente:
Copie el código de código de la siguiente manera:
Hilo inicial: 9: domingo 29 de septiembre 23:52:17 CST 2013
Hilo inicial: 10: domingo 29 de septiembre 23:52:19 CST 2013
Hilo inicial: 11: domingo 29 de septiembre 23:52:21 CST 2013
Tema terminado: 10: domingo 29 de septiembre 23:52:19 CST 2013
Hilo inicial: 12: domingo 29 de septiembre 23:52:23 CST 2013
Tema terminado: 11: domingo 29 de septiembre 23:52:21 CST 2013
Hilo inicial: 13: domingo 29 de septiembre 23:52:25 CST 2013
Tema terminado: 9: domingo 29 de septiembre 23:52:17 CST 2013
Hilo inicial: 14: domingo 29 de septiembre 23:52:27 CST 2013
Hilo inicial: 15: domingo 29 de septiembre 23:52:29 CST 2013
Tema terminado: 13: domingo 29 de septiembre a las 23:52:25 CST de 2013
Hilo inicial: 16: domingo 29 de septiembre 23:52:31 CST 2013
Tema terminado: 14: domingo 29 de septiembre 23:52:27 CST 2013
Hilo inicial: 17: domingo 29 de septiembre 23:52:33 CST 2013
Tema terminado: 12: domingo 29 de septiembre 23:52:23 CST 2013
Tema terminado: 16: domingo 29 de septiembre 23:52:31 CST 2013
Tema terminado: 15: domingo 29 de septiembre 23:52:29 CST 2013
Hilo inicial: 18: domingo 29 de septiembre 23:52:35 CST 2013
Tema terminado: 17: domingo 29 de septiembre 23:52:33 CST 2013
Tema terminado: 18: domingo 29 de septiembre 23:52:35 CST 2013
Las variables locales del subproceso almacenan una copia de la propiedad para cada subproceso. Puede utilizar el método get() de ThreadLocal para obtener el valor de una variable y utilizar el método set() para establecer el valor de una variable. Si se accede a una variable local de subproceso por primera vez y a la variable aún no se le ha asignado un valor, se llama al método inicialValue() para inicializar un valor para cada subproceso.
interminable
La clase ThreadLocal también proporciona el método remove() para eliminar el valor de la variable local almacenado en el hilo que llama a este método.
Además, la API de concurrencia de Java también proporciona la clase InheritableThreadLocal, que permite que el subproceso secundario reciba los valores iniciales de todas las variables locales del subproceso heredable para obtener el valor que pertenece al subproceso principal. Si el hilo A tiene una variable local de hilo, cuando el hilo A crea el hilo B, el hilo B tendrá la misma variable local de hilo que el hilo A. También puede anular childValue() para inicializar las variables locales del hilo secundario. Este método aceptará el valor de una variable local del subproceso pasada como parámetro desde el subproceso principal.
Usar doctrina
Este artículo está traducido del "Libro de cocina de concurrencia de Java 7" (D Gua Ge lo robó como "Colección de ejemplos de concurrencia de Java 7") y solo se utiliza como material de aprendizaje. No puede utilizarse con fines comerciales sin autorización.
Pequeño éxito
A continuación se muestra una versión completa de todo el código incluido en los ejemplos de esta sección.
Código completo de la clase UnsafeTask:
Copie el código de código de la siguiente manera:
paquete com.diguage.books.concurrencycookbook.chapter1.recipe9;
importar java.util.Fecha;
importar java.util.concurrent.TimeUnit;
/**
* Ejemplos en los que no se puede garantizar la seguridad de los subprocesos
* Fecha: 2013-09-23
* Hora: 23:58
*/
La clase pública UnsafeTask implementa Runnable {
fecha privada fecha de inicio;
@Anular
ejecución pública vacía() {
fecha de inicio = nueva fecha();
System.out.printf("Hilo inicial: %s: %s/n",
Thread.currentThread().getId(), fecha de inicio);
intentar {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} captura (Excepción interrumpida e) {
e.printStackTrace();
}
System.out.printf("Subproceso finalizado: %s: %s/n",
Thread.currentThread().getId(), fecha de inicio);
}
}
Código completo de la clase UnsafeMain:
Copie el código de código de la siguiente manera:
paquete com.diguage.books.concurrencycookbook.chapter1.recipe9;
importar java.util.concurrent.TimeUnit;
/**
* Ejemplo de hilo inseguro
* Fecha: 2013-09-24
* Hora: 00:04
*/
clase pública UnsafeMain {
público estático vacío principal (String [] argumentos) {
Tarea UnsafeTask = nueva UnsafeTask();
para (int i = 0; i < 10; i++) {
Hilo hilo = nuevo hilo (tarea);
hilo.start();
intentar {
Unidad de tiempo.SEGUNDOS.dormir(2);
} captura (Excepción interrumpida e) {
e.printStackTrace();
}
}
}
}
Código completo de la clase SafeTask:
Copie el código de código de la siguiente manera:
paquete com.diguage.books.concurrencycookbook.chapter1.recipe9;
importar java.util.Fecha;
importar java.util.concurrent.TimeUnit;
/**
* Utilice variables locales de subprocesos para garantizar la seguridad de los subprocesos.
* Fecha: 2013-09-29
* Hora: 23:34
*/
La clase pública SafeTask implementa Runnable {
ThreadLocal estático privado <Fecha> fecha de inicio = nuevo
ThreadLocal<Fecha>() {
@Anular
fecha protegida valor inicial() {
devolver nueva fecha();
}
};
@Anular
ejecución pública vacía() {
System.out.printf("Hilo inicial: %s: %s/n",
Thread.currentThread().getId(), startDate.get());
intentar {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} captura (Excepción interrumpida e) {
e.printStackTrace();
}
System.out.printf("Subproceso finalizado: %s: %s/n",
Thread.currentThread().getId(), startDate.get());
}
}
Código completo de la clase SafeMain:
Copie el código de código de la siguiente manera:
paquete com.diguage.books.concurrencycookbook.chapter1.recipe9;
importar java.util.concurrent.TimeUnit;
/**
* Ejemplo de hilo seguro
* Fecha: 2013-09-24
* Hora: 00:04
*/
clase pública SafeMain {
público estático vacío principal (String [] argumentos) {
Tarea SafeTask = nueva SafeTask();
para (int i = 0; i < 10; i++) {
Hilo hilo = nuevo hilo (tarea);
hilo.start();
intentar {
Unidad de tiempo.SEGUNDOS.dormir(2);
} captura (Excepción interrumpida e) {
e.printStackTrace();
}
}
}
}