Comment synchroniser ou verrouiller des variables en Java?

Permettez-moi d’utiliser ce petit et simple échantillon:

class Sample { private Ssortingng msg = null; public void newmsg(Ssortingng x){ msg = x; } public Ssortingng getmsg(){ Ssortingng temp = msg; msg = null; return temp; } } 

Supposons que la fonction newmsg() soit appelée par d’autres threads auxquels je n’ai pas access.

Je veux utiliser la méthode synchonize pour garantir que la chaîne msg ne soit utilisée que par une fonction par heure. En d’autres termes, la fonction newmsg() ne peut pas être exécutée en même temps que getmsg() .

C’est assez facile:

 class Sample { private Ssortingng message = null; private final Object lock = new Object(); public void newMessage(Ssortingng x) { synchronized (lock) { message = x; } } public Ssortingng getMessage() { synchronized (lock) { Ssortingng temp = message; message = null; return temp; } } } 

Notez que je n’ai pas non plus synchronisé ou synchronisé les méthodes à this égard. Je crois fermement que c’est une bonne idée d’acquérir uniquement des verrous sur des objects auxquels seul votre code a access, à moins que vous n’exposiez délibérément le verrou. Il est beaucoup plus facile de se rassurer que rien d’autre ne va acquérir des verrous dans un ordre différent de votre code, etc.

Pour cette fonctionnalité, mieux vaut ne pas utiliser de verrou du tout. Essayez une référence atomique.

 public class Sample { private final AtomicReference msg = new AtomicReference(); public void setMsg(Ssortingng x) { msg.set(x); } public Ssortingng getMsg() { return msg.getAndSet(null); } } 

Aucun verrou n’est requirejs et le code est plus simple à mon humble avis. En tout cas, il utilise une construction standard qui fait ce que vous voulez.

A partir de Java 1.5, il est toujours utile de considérer le package java.util.concurrent. Ils sont le mécanisme de locking de pointe en Java en ce moment. Le mécanisme de synchronisation est plus lourd que les classes java.util.concurrent.

L’exemple ressemblerait à ceci:

 import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Sample { private final Lock lock = new ReentrantLock(); private Ssortingng message = null; public void newmsg(Ssortingng msg) { try { lock.lock(); message = msg; } finally { lock.unlock(); } } public Ssortingng getmsg() { try { lock.lock(); Ssortingng temp = message; message = null; return temp; } finally { lock.unlock(); } } } 

Dans cet exemple simple, vous pouvez simplement mettre synchronized comme modificateur après public dans les deux signatures de méthode.

Des scénarios plus complexes nécessitent d’autres éléments.

Utilisez le mot-clé synchronized .

 class sample { private Ssortingng msg=null; public synchronized void newmsg(Ssortingng x){ msg=x; } public synchronized ssortingng getmsg(){ Ssortingng temp=msg; msg=null; return msg; } } 

L’utilisation du mot-clé synchronized sur les méthodes nécessite que les threads obtiennent un verrou sur l’instance de l’ sample . Ainsi, si un thread est dans newmsg() , aucun autre thread ne pourra obtenir un verrou sur l’instance de l’ sample , même s’il essayait d’appeler getmsg() .

D’un autre côté, l’utilisation de méthodes synchronized peut devenir un goulot d’étranglement si vos méthodes exécutent des opérations longues – tous les threads, même s’ils veulent appeler d’autres méthodes dans cet object pouvant être entrelacé, devront encore attendre.

IMO, dans votre exemple simple, il est correct d’utiliser des méthodes synchronisées car vous avez en fait deux méthodes qui ne doivent pas être entrelacées. Cependant, dans des circonstances différentes, il peut être plus logique de synchroniser un object de locking, comme le montre la réponse de Joh Skeet.

Si, à une autre occasion, vous synchronisez une collection plutôt qu’une chaîne, peut-être que vous parcourez la collection et que vous craignez une mutation, Java 5 offre:

  • CopyOnWriteArrayList
  • CopyOnWriteArraySet