62 votes

Comment CopyOnWriteArrayList peut-il être thread-safe?

J'ai pris un coup d'oeil dans OpenJDK code source de l' CopyOnWriteArrayList , et il semble que toutes les opérations d'écriture sont protégés par la même serrure et les opérations de lecture ne sont pas protégés à tous. Ce que je comprends, en vertu de JMM tous les accès à une variable (à la fois en lecture et en écriture) doivent être protégés par un verrou ou la réorganisation peut se produire des effets.

Par exemple, set(int, E) méthode contient ces lignes (sous clé):

/* 1 */ int len = elements.length;
/* 2 */ Object[] newElements = Arrays.copyOf(elements, len);
/* 3 */ newElements[index] = element;
/* 4 */ setArray(newElements);

L' get(int) méthode, d'autre part, seulement return get(getArray(), index);.

Dans ma compréhension de JMM, cela signifie qu' get peut observer le tableau dans un état incohérent si les énoncés 1 à 4 sont réorganisées comme 1-2(nouveau)-4-2(copyOf)-3.

Dois-je comprendre JMM de manière incorrecte ou est-il d'autres explications sur pourquoi est - CopyOnWriteArrayList est thread-safe?

77voto

Adamski Points 29884

Si vous regardez le sous-jacent de référence tableau vous verrez qu'il est marqué comme volatile. Lorsqu'une opération d'écriture se produit (comme dans l'extrait ci-dessus) ce volatile de référence est mis à jour dans la déclaration finale via setArray. Jusqu'à ce point, toutes les opérations de lecture sera de retour des éléments de l' ancienne copie de la matrice.

Le point important est que la matrice de mise à jour est une opération atomique et donc les lectures ne sont pas toujours voir le tableau dans un état cohérent.

L'avantage de ne prendre un verrou pour les opérations d'écriture est de l'amélioration du débit pour les lectures: C'est parce que les opérations d'écriture pour un CopyOnWriteArrayList peut potentiellement être très lente car ils impliquent la copie de l'ensemble de la liste.

21voto

mdma Points 33973

L'obtention de la matrice de référence est une opération atomique. Donc, les lecteurs verront le vieux tableau ou le nouveau tableau - de toute façon l'état est cohérent. (set(int,E) calcule le nouveau contenu du tableau avant de régler la référence, le tableau est constant lorsque le asignment est fait.)

Le tableau de référence lui-même est marqué comme volatile , de sorte que les lecteurs n'ont pas besoin d'utiliser un verrou pour voir les modifications à l'référencé tableau. (EDIT: Aussi, volatile garanties que la cession n'est pas re-commandés, ce qui conduirait à l'affectation d'être fait lorsque le tableau est peut-être dans un état incohérent.)

Le verrou d'écriture est nécessaire pour éviter les modifications simultanées, ce qui peut entraîner la table contenant les données incohérentes ou des changements de perdu.

Prograide.com

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.

Powered by:

X