J'ai lu que la copie de variable volatile sera partagée par tous les threads et une fois l'exécution terminée, la valeur de mise à jour sera obtenue par chaque thread, mais dans le programme suivant utilisant le pool de threads ne donnant pas la sortie que j'attendais, n'importe qui dis moi la raison?
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; class Task implements Runnable{ volatile int v1=10; private String name; public Task(String name) { this.name=name; } public synchronized void run() { v1=v1+10; System.out.println(name +"is entered "+v1); } } public class ThreadPoolTest { public static void main(String[] args) { Runnable r1 = new Task("Thread 1"); Runnable r2 = new Task("Thread 2"); Runnable r3 = new Task("Thread 3"); ExecutorService executor = Executors.newFixedThreadPool(5); executor.execute(r1); executor.execute(r2); executor.execute(r3); executor.shutdown(); } } outPut: Thread 1is entered 20 Thread 2is entered 20 Thread 3is entered 20 but if we change from volatile to static its giving below output: Thread 1is entered 20 Thread 3is entered 30 Thread 2is entered 40
3 Réponses :
Le résultat observé est correct car vous créez trois instances de Task
distinctes qui ont leur propre copie de la variable name
.
Ainsi, bien qu'elles soient volatiles
, les valeurs ne sont pas mises à jour par d'autres threads, le thread exécutant l'instance particulière est celui qui met à jour le champ v1
.
Si le v1
est rendu statique, alors il devient un membre de class
donc évidemment les threads mettront à jour la même copie de la variable v1
.
Au lieu d'utiliser synchronized
sur la méthode run
, nous pouvons utiliser AtomicInteger
pour mettre à jour et récupérer la valeur en toute sécurité.
class Task implements Runnable{ private final AtomicInteger v1 = new AtomicInteger(10); private String name; public void run() { System.out.println("Thread is: " + Thread.currentThread().getName() + " " + v1.addAndGet(10)); } } public class ThreadPoolTest{ public static void main(String[] args) { Runnable r1 = new Task(); ExecutorService executor = Executors.newFixedThreadPool(3); executor.execute(r1); executor.execute(r1); executor.execute(r1); executor.shutdown(); } }
Ceci est dû au fait que v1
est une variable d'instance et que chaque tâche a la sienne.
Donc, dans l'exemple, vous avez incrémenté v1
de différentes instances. static
n'est pas non plus fiable puisque vous synchronisez sur l'instance d'une Task
donc elle a toujours une condition de concurrence (par exemple, chaque thread peut lire v1
valeur de 10 même en cas de statique volatile
)
Vous avez probablement besoin de AtomicInteger
Oui, mieux vaut passer un AtomicInteger
aux constructeurs des Runnables. Évitez les variables globales / statiques.
Pour être bref, les variables volatiles ou atomiques sont les solutions pour résoudre l'incohérence ( erreurs de cohérence de la mémoire ) lorsque deux ou plusieurs threads tentent d'accéder à la même ressource (peut être statique / non -statique) simultanément.
https://docs.oracle.com/javase/tutorial /essential/concurrency/sync.html
Dans votre cas, vous ne partagez pas votre tâche avec plus d'un thread. donc pas besoin d'utiliser des mots clés volatils ou des variables atomiques. si vous souhaitez partager votre variable avec plus d'un thread, vous pouvez utiliser des variables atomiques sur volatile.
Que se passe-t-il alors lorsque vous ajoutez un mot-clé statique à une variable?
Les variables statiques sont disponibles pour tous les objets de cette classe.
https://docs.oracle.com/javase/tutorial /java/javaOO/classvars.html
https://docs.oracle.com/javase/tutorial /java/nutsandbolts/variables.html
Ce qui est tout à fait logique car vous créez 3 instances de la classe Task et son instance a sa propre variable v1. Ainsi, lorsque vous rendez ot statique, toutes les tâches partagent la même variable. C'est pourquoi ça va 20 30 40.
Ne
pas changer de volatile à statique
, à la place AJOUTER le statique tout en conservant le volatile (c.-à-d. Passer de «volatile» à «statique volatile»)