Quelle est la différence entre synchronized on lockObject et son utilisation comme verrou?

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 de bloc synchronisée.

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 l’utilisation de lockObject et son utilisation comme verrou? Cela semble être la même chose pour moi ..

Lorsque vous décidez d’utiliser le bloc synchronisé, comment décidez-vous quel object sera le verrou?

Personnellement, je ne verrouille presque jamais «ça». 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”, tout autre code qui connaît votre object peut choisir de le verrouiller. Bien qu’il soit peu probable que cela se produise, cela pourrait certainement être le cas – et pourrait causer des blocages ou simplement un blocage excessif.

Il n’y a rien de particulièrement magique dans ce que vous verrouillez – vous pouvez le considérer comme un jeton, efficacement. Quiconque verrouille avec le même jeton essaiera d’acquérir le même verrou. À moins que vous ne souhaitiez qu’un autre code puisse acquérir le même verrou, utilisez une variable privée. Je vous encourage également à rendre la variable final – je ne me souviens pas d’une situation où j’ai toujours voulu changer une variable de locking pendant la durée de vie d’un object.

J’ai eu la même question quand je lisais Java Concurrency In Practice, et je pensais append une perspective supplémentaire sur les réponses fournies par Jon Skeet et Spullara.

Voici un exemple de code qui bloquera même les setValue(int) “quick” setValue(int) / getValue() pendant que la doStuff(ValueHolder) s’exécute.

 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 d’utiliser this pour la synchronisation est que les autres classes peuvent se synchroniser sur une référence à votre classe (pas via, bien sûr). L’utilisation malveillante ou involontaire du mot-clé synchronized lors du locking de la référence de votre object peut entraîner un comportement médiocre de votre classe lors d’une utilisation concurrente, car une classe externe peut bloquer efficacement this méthodes synchronisées. interdire cela à l’exécution. Pour éviter cet écueil potentiel, vous devez effectuer une synchronisation sur un private final Object ou utiliser l’interface de 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’élément 67 de Effective Java Second Edition est d’éviter la synchronisation excessive, donc je synchroniserais sur un object de locking privé.

Chaque object en Java peut agir en tant que moniteur. Le choix de l’un dépend de la granularité souhaitée. Choisir ‘this’ présente l’avantage et l’inconvénient que d’autres classes pourraient également se synchroniser sur le même moniteur. Mon conseil est d’éviter d’utiliser directement le mot-clé synchronize et d’utiliser des constructions de la bibliothèque java.util.concurrency qui sont de niveau supérieur et ont une sémantique bien définie. Ce livre contient de très bons conseils d’experts remarquables:

Concurrence Java en pratique http://amzn.com/0321349601

Dans ce cas, peu importe quel object vous choisissez pour verrouiller. Mais vous devez toujours utiliser le même object pour le locking afin d’obtenir une synchronisation correcte. Le code ci-dessus n’assure pas une synchronisation correcte car vous utilisez une fois l’object “this” comme verrou et ensuite le verrou “lockObject”.