Les données partagées sont l’une des fonctionnalités les plus critiques des programmes concurrents. C'est un aspect très important qu'il s'agisse d'un objet qui hérite de la classe Thread ou d'un objet qui implémente l'interface Runnable.
Si vous créez un objet d'une classe qui implémente l'interface Runnable et utilisez cet objet pour démarrer une série de threads, tous ces threads partagent les mêmes propriétés. En d’autres termes, si un thread modifie une propriété, tous les threads restants sont affectés par la modification.
Parfois, nous préférons l'utiliser seul au sein d'un thread plutôt que de le partager avec d'autres threads démarrés avec le même objet. L'interface de concurrence Java fournit un mécanisme très clair pour répondre à cette exigence, appelé variables locales de thread. Les performances de ce mécanisme sont également très impressionnantes.
je le sais
Suivez les étapes indiquées ci-dessous pour terminer l’exemple de programme.
1. Tout d’abord, implémentez un programme avec les problèmes ci-dessus. Créez une classe nommée UnsafeTask et implémentez l'interface Runnable. Déclarez une propriété privée de type java.util.Date dans la classe. Le code est le suivant :
Copiez le code comme suit :
la classe publique UnsafeTask implémente Runnable {
date privée date de début ;
2. Implémentez la méthode run() de UnsafeTask, qui instancie l'attribut startDate et affiche sa valeur sur la console. Mettez en veille pendant une période aléatoire, puis imprimez à nouveau la valeur de l'attribut startDate sur la console. Le code est le suivant :
Copiez le code comme suit :
@Outrepasser
public void run() {
startDate = nouvelle Date();
System.out.printf("Thème de démarrage : %s : %s/n",
Thread.currentThread().getId(), startDate);
essayer {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Thread terminé : %s : %s/n",
Thread.currentThread().getId(), startDate);
}
3. Implémentez la classe principale du programme problématique. Créez une classe avec une méthode main(), UnsafeMain. Dans la méthode main(), créez un objet UnsafeTask et utilisez cet objet pour créer 10 objets Thread pour démarrer 10 threads. Au milieu de chaque fil, dormez 2 secondes. Le code est le suivant :
Copiez le code comme suit :
classe publique UnsafeMain {
public static void main (String[] arguments) {
Tâche UnsafeTask = new UnsafeTask();
pour (int je = 0; je < 10; i++) {
Thread thread = nouveau Thread (tâche);
thread.start();
essayer {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
4. D'après la logique ci-dessus, chaque thread a une heure de démarrage différente. Cependant, selon le journal de sortie ci-dessous, il existe de nombreuses valeurs temporelles identiques. comme suit:
Copiez le code comme suit :
Sujet de départ : 9 : dim 29 septembre 23:31:08 CST 2013
Sujet de départ : 10 : dim 29 septembre 23:31:10 CST 2013
Sujet de départ : 11 : dim 29 septembre 23:31:12 CST 2013
Sujet de départ : 12 : dim 29 septembre 23:31:14 CST 2013
Sujet terminé : 9 : dim 29 septembre 23:31:14 CST 2013
Sujet de départ : 13 : dim 29 septembre 23:31:16 CST 2013
Sujet terminé : 10 : dim 29 septembre 23:31:16 CST 2013
Sujet de départ : 14 : dim 29 septembre 23:31:18 CST 2013
Sujet terminé : 11 : dim 29 septembre 23:31:18 CST 2013
Sujet de départ : 15 : dim 29 septembre 23:31:20 CST 2013
Sujet terminé : 12 : dim 29 septembre 23:31:20 CST 2013
Sujet de départ : 16 : dim 29 septembre 23:31:22 CST 2013
Sujet de départ : 17 : dim. 29 septembre 23:31:24 CST 2013
Sujet terminé : 17 : dim 29 septembre 23:31:24 CST 2013
Sujet terminé : 15 : dim 29 septembre 23:31:24 CST 2013
Sujet terminé : 13 : dim 29 septembre 23:31:24 CST 2013
Sujet de départ : 18 : dim 29 septembre 23:31:26 CST 2013
Sujet terminé : 14 : dim 29 septembre 23:31:26 CST 2013
Sujet terminé : 18 : dim 29 septembre 23:31:26 CST 2013
Sujet terminé : 16 : dim 29 septembre 23:31:26 CST 2013
5. Comme indiqué ci-dessus, nous allons utiliser le mécanisme de variables locales du thread pour résoudre ce problème.
6. Créez une classe nommée SafeTask et implémentez l'interface Runnable. Le code est le suivant :
Copiez le code comme suit :
la classe publique SafeTask implémente Runnable {
7. Déclarez un objet de type ThreadLocal<Date> Lorsque l'objet est instancié, la méthode initialValue() est remplacée et la valeur de date réelle est renvoyée dans cette méthode. Le code est le suivant :
Copiez le code comme suit :
privé statique ThreadLocal<Date> startDate = nouveau
ThreadLocal<Date>() {
@Outrepasser
Date protégée valeur initiale() {
renvoie une nouvelle date ();
}
} ;
8. Implémentez la méthode run() de la classe SafeTask. Cette méthode est la même que la méthode run() de UnsafeTask, sauf que la méthode de l'attribut startDate est légèrement ajustée. Le code est le suivant :
Copiez le code comme suit :
@Outrepasser
public void run() {
System.out.printf("Thème de démarrage : %s : %s/n",
Thread.currentThread().getId(), startDate.get());
essayer {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Thread terminé : %s : %s/n",
Thread.currentThread().getId(), startDate.get());
}
9. La classe principale de cet exemple sécurisé est fondamentalement la même que la classe principale du programme non sécurisé, sauf que UnsafeTask doit être modifié en SafeTask. Le code spécifique est le suivant :
Copiez le code comme suit :
classe publique SafeMain {
public static void main (String[] arguments) {
Tâche SafeTask = new SafeTask();
pour (int je = 0; je < 10; i++) {
Thread thread = nouveau Thread (tâche);
thread.start();
essayer {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
10. Exécutez le programme et analysez les différences entre les deux entrées.
Afin de normaliser la dénomination des classes, la dénomination de la classe principale dans cet article est légèrement différente du texte original. De plus, le programme original et la description textuelle sont incohérents. Il doit s'agir d'une erreur matérielle.
sais pourquoi
Vous trouverez ci-dessous le résultat de l'exécution de l'exemple de sécurité. À partir des résultats, il peut être facilement vu que chaque thread a une valeur d'attribut startDate appartenant au thread respectif. L'entrée du programme est la suivante :
Copiez le code comme suit :
Sujet de départ : 9 : dim 29 septembre 23:52:17 CST 2013
Sujet de départ : 10 : dim 29 septembre 23:52:19 CST 2013
Sujet de départ : 11 : dim 29 septembre 23:52:21 CST 2013
Sujet terminé : 10 : dim 29 septembre 23:52:19 CST 2013
Sujet de départ : 12 : dim 29 septembre 23:52:23 CST 2013
Sujet terminé : 11 : dim 29 septembre 23:52:21 CST 2013
Sujet de départ : 13 : dim 29 septembre 23:52:25 CST 2013
Sujet terminé : 9 : dim 29 septembre 23:52:17 CST 2013
Sujet de départ : 14 : dim 29 septembre 23:52:27 CST 2013
Sujet de départ : 15 : dim 29 septembre 23:52:29 CST 2013
Sujet terminé : 13 : dim 29 septembre 23:52:25 CST 2013
Sujet de départ : 16 : dim 29 septembre 23:52:31 CST 2013
Sujet terminé : 14 : dim 29 septembre 23:52:27 CST 2013
Sujet de départ : 17 : dim 29 septembre 23:52:33 CST 2013
Sujet terminé : 12 : dim 29 septembre 23:52:23 CST 2013
Sujet terminé : 16 : dim 29 septembre 23:52:31 CST 2013
Sujet terminé : 15 : dim 29 septembre 23:52:29 CST 2013
Sujet de départ : 18 : dim 29 septembre 23:52:35 CST 2013
Sujet terminé : 17 : dim 29 septembre 23:52:33 CST 2013
Sujet terminé : 18 : dim 29 septembre 23:52:35 CST 2013
Les variables locales de thread stockent une copie de la propriété pour chaque thread. Vous pouvez utiliser la méthode get() de ThreadLocal pour obtenir la valeur d'une variable et utiliser la méthode set() pour définir la valeur d'une variable. Si une variable locale de thread est accédée pour la première fois et qu'aucune valeur n'a encore été attribuée à la variable, la méthode initialValue() est appelée pour initialiser une valeur pour chaque thread.
sans fin
La classe ThreadLocal fournit également la méthode Remove() pour supprimer la valeur de la variable locale stockée dans le thread qui appelle cette méthode.
De plus, l'API de concurrence Java fournit également la classe InheritableThreadLocal, qui permet au thread enfant de recevoir les valeurs initiales de toutes les variables locales du thread héritable pour obtenir la valeur appartenant au thread parent. Si le thread A a une variable locale de thread, lorsque le thread A crée le thread B, le thread B aura la même variable locale de thread que le thread A. Vous pouvez également remplacer childValue() pour initialiser les variables locales du thread enfant. Cette méthode acceptera la valeur d'une variable locale du thread passée en paramètre depuis le thread parent.
Utiliser la doctrine
Cet article est traduit du "Java 7 Concurrency Cookbook" (D Gua Ge l'a volé sous le nom de "Java7 Concurrency Sample Collection") et n'est utilisé que comme matériel d'apprentissage. Il ne peut être utilisé à des fins commerciales sans autorisation.
Petit succès
Vous trouverez ci-dessous une version complète de tout le code inclus dans les exemples de cette section.
Code complet de la classe UnsafeTask :
Copiez le code comme suit :
package com.diguage.books.concurrencycookbook.chapter1.recipe9 ;
importer java.util.Date ;
importer java.util.concurrent.TimeUnit ;
/**
* Exemples où la sécurité des threads ne peut pas être garantie
* Date : 2013-09-23
* Heure : 23h58
*/
la classe publique UnsafeTask implémente Runnable {
date privée date de début ;
@Outrepasser
public void run() {
startDate = nouvelle Date();
System.out.printf("Thème de démarrage : %s : %s/n",
Thread.currentThread().getId(), startDate);
essayer {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Thread terminé : %s : %s/n",
Thread.currentThread().getId(), startDate);
}
}
Code complet de la classe UnsafeMain :
Copiez le code comme suit :
package com.diguage.books.concurrencycookbook.chapter1.recipe9 ;
importer java.util.concurrent.TimeUnit ;
/**
* Exemple de fil non sécurisé
* Date : 2013-09-24
* Heure : 00:04
*/
classe publique UnsafeMain {
public static void main (String[] arguments) {
Tâche UnsafeTask = new UnsafeTask();
pour (int je = 0; je < 10; i++) {
Thread thread = nouveau Thread (tâche);
thread.start();
essayer {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Code complet de la classe SafeTask :
Copiez le code comme suit :
package com.diguage.books.concurrencycookbook.chapter1.recipe9 ;
importer java.util.Date ;
importer java.util.concurrent.TimeUnit ;
/**
* Utilisez des variables locales de thread pour garantir la sécurité des threads
* Date : 2013-09-29
* Heure : 23h34
*/
la classe publique SafeTask implémente Runnable {
privé statique ThreadLocal<Date> startDate = nouveau
ThreadLocal<Date>() {
@Outrepasser
Date protégée valeur initiale() {
renvoie une nouvelle date ();
}
} ;
@Outrepasser
public void run() {
System.out.printf("Thème de démarrage : %s : %s/n",
Thread.currentThread().getId(), startDate.get());
essayer {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Thread terminé : %s : %s/n",
Thread.currentThread().getId(), startDate.get());
}
}
Code complet de la classe SafeMain :
Copiez le code comme suit :
package com.diguage.books.concurrencycookbook.chapter1.recipe9 ;
importer java.util.concurrent.TimeUnit ;
/**
* Exemple de fil sécurisé
* Date : 2013-09-24
* Heure : 00:04
*/
classe publique SafeMain {
public static void main (String[] arguments) {
Tâche SafeTask = new SafeTask();
pour (int je = 0; je < 10; i++) {
Thread thread = nouveau Thread (tâche);
thread.start();
essayer {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}