Quelle est la différence entre la synchronisation d'une méthode statique et une méthode non statique en Java? Quelqu'un peut-il expliquer s'il vous plaît avec un exemple. De plus, y a-t-il une différence entre la synchronisation d'une méthode et la synchronisation d'un bloc de code?
Je vais essayer d'ajouter un exemple pour rendre cela plus clair.
Comme on l'a mentionné, synchronisé en Java est une implémentation du concept Monitor . Lorsque vous marquez un bloc de code comme synchronisé, vous utilisez un objet en tant que paramètre. Lorsqu'un thread en cours d'exécution parvient à un tel bloc de code, il doit d'abord attendre qu'il n'y ait plus d'autre thread en cours d'exécution dans un bloc synchronisé sur le même objet.
Object a = new Object();
Object b = new Object();
...
synchronized(a){
doStuff();
}
...
synchronized(b){
doSomeStuff();
}
...
synchronized(a){
doOtherStuff();
}
Dans l'exemple ci-dessus, un thread exécutant doOtherStuff()
empêcherait un autre thread d'entrer dans le bloc de code protégeant doStuff()
. Cependant, un thread pourrait entrer dans le bloc autour de doSomeStuff()
sans problème car il est synchronisé sur Object b
, et non pas Object a
.
Lorsque vous utilisez le modificateur synchronisé sur une méthode d'instance (une méthode non statique), cela ressemble beaucoup à un bloc synchronisé avec "ceci" comme argument. Donc, dans l'exemple suivant, methodA()
et methodB()
agiront de la même manière:
public synchronized void methodA() {
doStuff();
}
...
public void methodB() {
synchronized(this) {
doStuff();
}
}
Notez que si vous avez une methodC()
dans cette classe qui n'est pas synchronisée et ne possède pas de bloc synchronisé, rien n'empêchera un thread d'entrer cette méthode et une programmation négligente pourrait permettre à ce thread d'accéder à du code non sécurisé dans l'objet.
Si vous avez une méthode statique avec le modificateur synchronized, c'est pratiquement la même chose que d'avoir un bloc synchronisé avec ClassName.class
comme argument (si vous avez un objet de cette classe, ClassName cn = new ClassName();
, vous pouvez accéder à cet objet avec Class c = cn.getClass();
)
class ClassName {
public void static synchronized staticMethodA() {
doStaticStuff();
}
public static void staticMethodB() {
synchronized(ClassName.class) {
doStaticStuff();
}
}
public void nonStaticMethodC() {
synchronized(this.getClass()) {
doStuff();
}
}
public static void unSafeStaticMethodD() {
doStaticStuff();
}
}
Ainsi, dans l'exemple ci-dessus, staticMethodA()
et staticMethodB()
agissent de la même manière. Un thread en cours d'exécution ne pourra pas non plus accéder au bloc de code dans nonStaticMethodC()
puisqu'il se synchronise sur le même objet.
Cependant, il est important de savoir que rien n'empêchera un thread en cours d'exécution d'accéder à unSafeStaticMethodD()
. Même si nous disons qu'une méthode statique "se synchronise sur l'objet Class", cela ne signifie pas qu'elle synchronise tous les accès aux méthodes de cette classe. Cela signifie simplement qu'il utilise l'objet Class pour se synchroniser. Un accès non sécurisé est toujours possible.
En bref, si vous synchronisez sur une méthode statique, vous synchroniserez sur la classe (objet) et non sur une instance (objet). Cela signifie que pendant l'exécution d'une méthode statique, toute la classe est bloquée. Donc, les autres méthodes synchronisées statiques sont également bloquées.
La synchronisation en Java est essentiellement une implémentation de moniteurs . Lors de la synchronisation d'une méthode non statique, le moniteur appartient à l'instance. Lors de la synchronisation sur une méthode statique, le moniteur appartient à la classe. La synchronisation d'un bloc de code est la même idée, mais le moniteur appartient à l'objet spécifié. Si vous pouvez vous en sortir, les blocs synchronisés sont préférables car ils minimisent le temps que chaque thread passe dans la section critical
Il n'y a pratiquement aucune différence entre la synchronisation d'un bloc et la synchronisation d'une méthode. Fondamentalement:
void synchronized m() {...}
est le même que
void m() { synchronized(this) {...} }
Par comparaison, une méthode synchronisée statique est identique à:
static void m() { synchronized(MyClass.class) {...} }
Java Thread acquiert un verrou de niveau objet lorsqu'il entre dans une instance méthode Java synchronisée et acquiert un verrou de niveau Classe lorsqu'il entre dans méthode Java synchronisée statique. En utilisant bloc synchronisé, vous ne pouvez verrouiller que les sections de code critiques et éviter de verrouiller toute la méthode, ce qui pourrait éventuellement dégrader les performances.
Mec, juste un indice. Pas lié à votre question:
Si l'une des méthodes * Stuff () le fait
this.a= /*yet another*/ new Object();
ou
this.b= /*yet another*/ new Object();
alors vous êtes foutu. Parce que le verrou est à l'intérieur de la valeur, pas à l'intérieur de la référence. Voir Références synchronisées Java
De javadoc https://docs.Oracle.com/javase/tutorial/essential/concurrency/locksync.html
lorsqu'une méthode statique synchronisée est appelée, puisqu'un procédé statique est associé à une classe, pas à un objet. Dans ce cas, le thread acquiert le verrou intrinsèque pour l'objet Class associé à la classe. Ainsi, l'accès aux champs statiques de la classe est contrôlé par un verrou distinct de celui de toute instance de la classe.
public static synchronized void getInstance(){}
Lorsque nous acquérons un verrou sur une classe, nous acquérons en fait un verrou sur une instance de classe "Classe" qui ne représente qu'un seul pour toutes les instances de la classe.
public synchronized void getInstance(){}
nous pouvons créer plusieurs objets d'une classe et chaque objet sera associé à un verrou.