Je suis d'accord avec l'un de Jean du commentaire: Vous devez toujours utiliser un blocage définitif de mannequin tout en accédant à un non-dernière variable pour éviter des incohérences dans le cas de la variable de référence des changements. Donc dans tous les cas, et comme une première règle de base:
Règle n ° 1: Si un champ n'est pas définitive, utilisez toujours un (privé) blocage définitif de mannequin.
Raison n ° 1: Vous maintenez le verrou et le changement de la variable de référence par vous-même. Un autre thread en attente à l'extérieur de la synchronisation de verrouillage sera en mesure d'entrer dans le gardé bloc.
Raison n ° 2: Vous maintenez le verrou et un autre thread changements de la variable de référence. Le résultat est le même: un Autre thread peut entrer dans le gardé bloc.
Mais lors de l'utilisation d'un blocage définitif de mannequin, il y a un autre problème: Vous risquez d'obtenir des données erronées, parce que votre non-objet final ne seront synchronisées avec la RAM lors de l'appel de synchroniser(objet). Donc, c'est la deuxième règle de base:
Règle n ° 2: Lorsque le verrouillage d'un non-objet final vous avez toujours besoin de faire les deux: à l'Aide d'un blocage définitif mannequin et le verrouillage de la non-objet final pour le bien de la RAM de synchronisation. (La seule alternative sera de déclarer tous les champs de l'objet comme étant volatile!)
Ces serrures sont également appelés "imbriqués les verrous". Notez que vous devez appeler toujours dans le même ordre, sinon vous obtiendrez un mort de verrouillage:
public class X {
private final LOCK;
private Object o;
public void setO(Object o){
this.o = o;
}
public void x() {
synchronized (LOCK) {
synchronized(o){
//do something with o...
}
}
}
}
Comme vous pouvez le voir j'écris les deux verrous directement sur la même ligne, parce qu'ils appartiennent toujours ensemble. Comme cela, vous pourriez même faire 10 imbrication de serrures:
synchronized (LOCK1) {
synchronized (LOCK2) {
synchronized (LOCK3) {
synchronized (LOCK4) {
//entering the locked space
}
}
}
}
Notez que ce code ne cassera pas si vous venez d'acquérir un intérieur de verrouillage comme synchronized (LOCK3)
par un autre fils. Mais il va se casser si vous appelez dans un autre thread quelque chose comme ceci:
synchronized (LOCK4) {
synchronized (LOCK1) { //dead lock!
synchronized (LOCK3) {
synchronized (LOCK2) {
//will never enter here...
}
}
}
}
Il y a seulement une solution de contournement autour de ces imbriqués les verrous lors de la manipulation de non-final champs:
Règle #2 - Alternative: Déclarer tous les champs de l'objet comme étant volatile. (Je ne parlerai pas ici sur les inconvénients de le faire, par exemple en empêchant tout stockage en x au niveau des caches de même pour les lectures, etc...)
Donc aioobe est tout à fait juste: il suffit d'utiliser java.util.de façon concomitante. Ou commencer à comprendre tout à propos de la synchronisation et de le faire par vous-même avec imbriqués les verrous. ;)
Pour plus de détails pourquoi la synchronisation sur la non-final champs pauses, regardez dans mon cas de test: http://stackoverflow.com/a/21460055/2012947
Et pour plus de détails pourquoi vous avez besoin de synchronisés à tout en raison de la RAM et des caches d'avoir un coup d'oeil ici: http://stackoverflow.com/a/21409975/2012947