Quand utilisons-nous AtomicReference
?
Est-il nécessaire de créer des objets dans tous les programmes multithreads ?
Donnez un exemple simple où AtomicReference doit être utilisé.
Quand utilisons-nous AtomicReference
?
Est-il nécessaire de créer des objets dans tous les programmes multithreads ?
Donnez un exemple simple où AtomicReference doit être utilisé.
Je ne parlerai pas beaucoup. Mes amis respectés ont déjà apporté leur précieuse contribution. Le code de fonctionnement complet figurant à la fin de ce blog devrait lever toute confusion. Il s'agit d'un petit programme de réservation de places de cinéma dans un scénario multithread.
Voici quelques faits élémentaires importants. 1> Les différents threads ne peuvent se disputer que les variables d'instance et les variables membres statiques dans l'espace du tas. 2> Les lectures et écritures volatiles sont complètement atomiques et sérialisées avant et seulement faites à partir de la mémoire. En disant cela, je veux dire que toute lecture suivra l'écriture précédente en mémoire. Et toute écriture suivra la lecture précédente de la mémoire. Ainsi, tout thread travaillant avec un volatile verra toujours la valeur la plus récente. AtomicReference utilise cette propriété de volatile.
Voici une partie du code source de AtomicReference. AtomicReference fait référence à un objet. Cette référence est une variable membre volatile dans l'instance AtomicReference comme ci-dessous.
private volatile V value;
get() renvoie simplement la dernière valeur de la variable (comme les volatiles le font à la manière de "happens before").
public final V get()
La méthode suivante est la plus importante de AtomicReference.
public final boolean compareAndSet(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}
La méthode compareAndSet(expect, update) appelle la méthode compareAndSwapObject() de la classe non sécurisée de Java. Cet appel de méthode de unsafe fait appel à l'appel natif, qui invoque une seule instruction au processeur. "expect" et "update" font chacun référence à un objet.
Si et seulement si la variable d'instance AtomicReference "value" fait référence au même objet que celui auquel fait référence "expect", "update" est attribué à cette variable d'instance maintenant, et "true" est renvoyé. Sinon, false est renvoyé. Le tout est fait de manière atomique. Aucun autre thread ne peut intercepter entre les deux. Comme il s'agit d'une opération monoprocesseur (magie de l'architecture informatique moderne), elle est souvent plus rapide que l'utilisation d'un bloc synchronisé. Mais N'oubliez pas que lorsque plusieurs variables doivent être mises à jour de manière atomique, AtomicReference ne vous sera d'aucune utilité.
Je voudrais ajouter un code d'exécution à part entière, qui peut être exécuté dans eclipse. Cela permettrait de dissiper de nombreuses confusions. Ici 22 utilisateurs (MyTh threads) essaient de réserver 20 places. Voici un extrait de code suivi du code complet.
Extrait de code où 22 utilisateurs essaient de réserver 20 places.
for (int i = 0; i < 20; i++) {// 20 seats
seats.add(new AtomicReference<Integer>());
}
Thread[] ths = new Thread[22];// 22 users
for (int i = 0; i < ths.length; i++) {
ths[i] = new MyTh(seats, i);
ths[i].start();
}
Voici le code d'exécution complet.
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public class Solution {
static List<AtomicReference<Integer>> seats;// Movie seats numbered as per
// list index
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
seats = new ArrayList<>();
for (int i = 0; i < 20; i++) {// 20 seats
seats.add(new AtomicReference<Integer>());
}
Thread[] ths = new Thread[22];// 22 users
for (int i = 0; i < ths.length; i++) {
ths[i] = new MyTh(seats, i);
ths[i].start();
}
for (Thread t : ths) {
t.join();
}
for (AtomicReference<Integer> seat : seats) {
System.out.print(" " + seat.get());
}
}
/**
* id is the id of the user
*
* @author sankbane
*
*/
static class MyTh extends Thread {// each thread is a user
static AtomicInteger full = new AtomicInteger(0);
List<AtomicReference<Integer>> l;//seats
int id;//id of the users
int seats;
public MyTh(List<AtomicReference<Integer>> list, int userId) {
l = list;
this.id = userId;
seats = list.size();
}
@Override
public void run() {
boolean reserved = false;
try {
while (!reserved && full.get() < seats) {
Thread.sleep(50);
int r = ThreadLocalRandom.current().nextInt(0, seats);// excludes
// seats
//
AtomicReference<Integer> el = l.get(r);
reserved = el.compareAndSet(null, id);// null means no user
// has reserved this
// seat
if (reserved)
full.getAndIncrement();
}
if (!reserved && full.get() == seats)
System.out.println("user " + id + " did not get a seat");
} catch (InterruptedException ie) {
// log it
}
}
}
}
Un autre exemple simple consiste à effectuer une modification sûre dans un objet de session.
public PlayerScore getHighScore() {
ServletContext ctx = getServletConfig().getServletContext();
AtomicReference<PlayerScore> holder
= (AtomicReference<PlayerScore>) ctx.getAttribute("highScore");
return holder.get();
}
public void updateHighScore(PlayerScore newScore) {
ServletContext ctx = getServletConfig().getServletContext();
AtomicReference<PlayerScore> holder
= (AtomicReference<PlayerScore>) ctx.getAttribute("highScore");
while (true) {
HighScore old = holder.get();
if (old.score >= newScore.score)
break;
else if (holder.compareAndSet(old, newScore))
break;
}
}
Fuente: http://www.ibm.com/developerworks/library/j-jtp09238/index.html
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.