web-dev-qa-db-fra.com

Comment synchroniser ou verrouiller des variables en Java?

Permettez-moi d'utiliser cet exemple simple et petit:

class Sample {
    private String msg = null;

    public void newmsg(String x){
        msg = x;
    }

    public String getmsg(){
        String temp = msg;
        msg = null;
        return temp;
    }
}

Supposons que la fonction newmsg() est appelée par d'autres threads auxquels je n'ai pas accès.

Je veux utiliser la méthode synchonize pour garantir que la chaîne msg n'est utilisée que par une fonction à la fois. En d'autres termes, la fonction newmsg() ne peut pas s'exécuter en même temps que getmsg().

65
Winter

C'est assez facile:

class Sample {
    private String message = null;
    private final Object lock = new Object();

    public void newMessage(String x) {
        synchronized (lock) {
            message = x;
        }
    }

    public String getMessage() {
        synchronized (lock) {
            String temp = message;
            message = null;
            return temp;
        }
    }
}

Notez que je n'ai pas synchronisé les méthodes elles-mêmes ou synchronisé sur this. Je crois fermement que c’est une bonne idée d’acquérir uniquement des verrous sur des objets auxquels seul votre code a accès, à moins que vous n'exposiez délibérément le verrou. Il est beaucoup plus facile de vous assurer que rien d'autre ne permettra d'acquérir des verrous dans un ordre différent de celui de votre code, etc.

152
Jon Skeet

Pour cette fonctionnalité, il est préférable de ne pas utiliser de verrou. Essayez une référence atomique.

public class Sample {
    private final AtomicReference<String> msg = new AtomicReference<String>();

    public void setMsg(String x) {
        msg.set(x);
    }

    public String getMsg() {
        return msg.getAndSet(null);
    }
}

Aucun verrou requis et le code est plus simple à mon humble avis. Dans tous les cas, il utilise une construction standard qui fait ce que vous voulez.

49
Peter Lawrey

De Java 1.5, c’est toujours une bonne idée de considérer le package Java.util.concurrent. C’est le mécanisme de verrouillage de pointe dans Java pour le moment. Le mécanisme de synchronisation est plus lourd que les classes Java.util.concurrent.

L'exemple ressemblerait à quelque chose comme ceci:

import Java.util.concurrent.locks.Lock;
import Java.util.concurrent.locks.ReentrantLock;

public class Sample {

    private final Lock lock = new ReentrantLock();

    private String message = null;

    public void newmsg(String msg) {
        lock.lock();
        try {
            message = msg;
        } finally {
            lock.unlock();
        }
    }

    public String getmsg() {
        lock.lock();
        try {
            String temp = message;
            message = null;
            return temp;
        } finally {
            lock.unlock();
        }
    }
}
9
Niclas Meier

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

Les scénarios plus complexes nécessitent d'autres éléments.

3
gd1

Utilisez le mot clé synchronized.

class sample {
    private String msg=null;

    public synchronized void newmsg(String x){
        msg=x;
    }

    public synchronized string getmsg(){
        String temp=msg;
        msg=null;
        return msg;
    }
}

L'utilisation du mot clé synchronized sur les méthodes nécessitera que les threads obtiennent un verrou sur l'instance de sample. Ainsi, si l'un des threads est dans newmsg(), aucun autre thread ne pourra verrouiller l'instance de sample, même si elle essayait d'appeler getmsg().

Par ailleurs, l’utilisation des méthodes synchronized peut devenir un goulot d’étranglement si vos méthodes effectuent des opérations longues. Tous les threads, même s’ils souhaitent invoquer d’autres méthodes dans cet objet pouvant être entrelacées, devront encore attendre. .

OMI, dans votre exemple simple, il est correct d'utiliser des méthodes synchronisées car vous avez en réalité deux méthodes qui ne doivent pas être entrelacées. Cependant, dans des circonstances différentes, il pourrait être plus judicieux de synchroniser un objet verrou, comme indiqué dans la réponse de Joh Skeet.

2

Si, à une autre occasion, vous synchronisez une collection plutôt qu'une chaîne, vous êtes peut-être en train de parcourir la collection et êtes inquiet de la mutation de celle-ci, Java 5 offre:

0
SK9