Je connais la différence entre la méthode synchronisée et le bloc synchronisé mais je ne suis pas sûr de la partie du bloc synchronisé.
En supposant que j'ai ce code
class Test {
private int x=0;
private Object lockObject = new Object();
public void incBlock() {
synchronized(lockObject) {
x++;
}
System.out.println("x="+x);
}
public void incThis() { // same as synchronized method
synchronized(this) {
x++;
}
System.out.println("x="+x);
}
}
Dans ce cas, quelle est la différence entre utiliser lockObject et utiliser this comme verrou? Il me semble que c'est la même chose ..
Lorsque vous décidez d'utiliser un bloc synchronisé, comment décidez-vous quel objet sera le verrou?
Personnellement, je ne verrouille presque jamais "ceci". Je verrouille généralement une référence privée que je sais qu'aucun autre code ne va verrouiller. Si vous verrouillez "this", alors any un autre code qui connaît votre objet peut choisir de le verrouiller. Bien qu'il soit peu probable que cela se produise, cela pourrait certainement le faire - et pourrait provoquer des blocages ou tout simplement un verrouillage excessif.
Il n'y a rien de particulièrement magique dans ce que vous verrouillez - vous pouvez le considérer comme un jeton, en fait. Toute personne verrouillant avec le même jeton tentera d'acquérir le même verrou. Sauf si vous voulez un autre code pour pouvoir acquérir le même verrou, utilisez une variable privée. Je aussi vous encourage à créer la variable final
- Je ne me souviens pas d'une situation où j'ai jamais voulu changer une variable de verrouillage la durée de vie d'un objet.
J'ai eu cette même question lorsque je lisais Java Concurrence en pratique, et je pensais ajouter une perspective supplémentaire sur les réponses fournies par Jon Skeet et spullara.
Voici un exemple de code qui bloquera même les méthodes "rapides" setValue(int)
/getValue()
pendant l'exécution de la méthode doStuff(ValueHolder)
.
public class ValueHolder {
private int value = 0;
public synchronized void setValue(int v) {
// Or could use a sychronized(this) block...
this.value = 0;
}
public synchronized int getValue() {
return this.value;
}
}
public class MaliciousClass {
public void doStuff(ValueHolder holder) {
synchronized(holder) {
// Do something "expensive" so setter/getter calls are blocked
}
}
}
L'inconvénient de l'utilisation de this
pour la synchronisation est que d'autres classes peuvent se synchroniser sur une référence à votre classe (pas via this
, bien sûr). Une utilisation malveillante ou involontaire du mot clé synchronized
lors du verrouillage de la référence de votre objet peut entraîner un mauvais comportement de votre classe en cas d'utilisation simultanée, car une classe externe peut bloquer efficacement vos méthodes synchronisées this
- et il existe rien que vous puissiez faire (dans votre classe) pour interdire cela lors de l'exécution. Pour éviter cet écueil potentiel, vous devez synchroniser sur un private final Object
ou utilisez l'interface Lock
dans Java.util.concurrent.locks
.
Pour cet exemple simple, vous pouvez alternativement utiliser un AtomicInteger
plutôt que de synchroniser le setter/getter.
L'article 67 de Effectif Java Deuxième Édition est Évitez une synchronisation excessive, donc je synchroniserais sur un objet de verrouillage privé.
Chaque objet dans Java peut agir comme un moniteur. Le choix de celui-ci dépend de la granularité que vous souhaitez. Choisir "ceci" a l'avantage et le désavantage que d'autres classes pourraient également se synchroniser sur le même moniteur. Mon Le conseil est cependant d'éviter d'utiliser directement le mot clé synchronize et d'utiliser à la place des constructions de la bibliothèque Java.util.concurrency qui sont de niveau supérieur et qui ont une sémantique bien définie. Ce livre contient de très bons conseils d'experts très réputés:
Concurrence Java en pratique http://amzn.com/0321349601
Dans ce cas, peu importe l'objet que vous choisissez pour verrouiller. Mais vous devez toujours utiliser le même objet pour le verrouillage afin d'obtenir une synchronisation correcte. Le code ci-dessus n'assure pas une synchronisation correcte car vous utilisez une fois l'objet "this" comme verrou et ensuite le "lockObject" comme verrou.