J'ai commencé à apprendre la synchronisation dans le threading.
Méthode synchronisée:
public class Counter{
private static int count = 0;
public static synchronized int getCount(){
return count;
}
public synchronized setCount(int count){
this.count = count;
}
}
Bloc synchronisé:
public class Singleton{
private static volatile Singleton _instance;
public static Singleton getInstance(){
if(_instance == null){
synchronized(Singleton.class){
if(_instance == null)
_instance = new Singleton();
}
}
return _instance;
}
Quand devrais-je utiliser la méthode Synchronized et le bloc Synchronized? Pourquoi le bloc synchronisé est-il meilleur que la méthode synchronisée?
Ce n'est pas une question de mieux, mais de différent.
Lorsque vous synchronisez une méthode, vous effectuez une synchronisation avec l'objet lui-même. Dans le cas d'une méthode statique, vous synchronisez avec la classe de l'objet. Donc, les deux morceaux de code suivants s'exécutent de la même manière:
public synchronized int getCount() {
// ...
}
Ceci est juste comme vous avez écrit ceci.
public int getCount() {
synchronized (this) {
// ...
}
}
Si vous souhaitez contrôler la synchronisation avec un objet spécifique, ou si vous souhaitez seulement faire partie d'une méthode à synchroniser avec l'objet, spécifiez un synchronized
bloc. Si vous utilisez le mot clé synchronized
dans la déclaration de méthode, la méthode entière sera synchronisée avec l'objet ou la classe.
Bien que cela ne pose généralement pas de problème, du point de vue de la sécurité, il est préférable d’utiliser synchronisé sur un objet privé plutôt que de le placer sur une méthode.
Le fait de le mettre sur la méthode signifie que vous utilisez le verrou de l'objet lui-même pour assurer la sécurité du thread. Avec ce type de mécanisme, il est possible pour un utilisateur malveillant de votre code d’obtenir également le verrou sur votre objet et de le conserver indéfiniment, bloquant ainsi d’autres threads. Un utilisateur non malveillant peut effectivement faire la même chose par inadvertance.
Si vous utilisez le verrou d'un membre de données privé, vous pouvez empêcher cela, car il est impossible pour un utilisateur malveillant d'obtenir le verrou sur votre objet privé.
private final Object lockObject = new Object();
public void getCount() {
synchronized( lockObject ) {
...
}
}
Cette technique est mentionnée dans Effective Java (2nd Ed), Item # 70 de Bloch)
La différence réside dans l'acquisition du verrou:
méthode synchronisée acquiert un verrou sur l’ensemble de l’objet. Cela signifie qu'aucun autre thread ne peut utiliser une méthode synchronisée dans l'objet entier pendant que la méthode est exécutée par un seul thread.
les blocs synchronisés acquièrent un verrou dans l'objet entre parenthèses après le mot clé synchronized. Cela signifie qu'aucun autre thread ne peut acquérir un verrou sur l'objet verrouillé jusqu'à ce que le bloc synchronisé se termine.
Donc, si vous voulez verrouiller tout l'objet, utilisez une méthode synchronisée. Si vous souhaitez que d'autres parties de l'objet restent accessibles à d'autres threads, utilisez le bloc synchronisé.
Si vous choisissez soigneusement l'objet verrouillé, les blocs synchronisés réduiront le nombre de conflits, car l'ensemble de la classe/objet n'est pas bloqué.
Cela s'applique de la même manière aux méthodes statiques: une méthode statique synchronisée acquiert un verrou dans l'objet de classe entier, tandis qu'un bloc synchronisé dans une méthode statique acquiert un verrou dans l'objet entre parenthèses.
La différence entre bloc synchronisé et méthode synchronisée est la suivante:
bloc synchronisé:synchronized(this){}
méthode synchronisée:public synchronized void fun(){}
Définir 'mieux'. Un bloc synchronisé n’est que meilleur car il vous permet de:
Maintenant, votre exemple spécifique est un exemple du motif verrouillage vérifié qui est suspect (dans les versions antérieures Java), il était cassé et il est facile de le faire mal ).
Si votre initialisation est bon marché, il serait peut-être préférable de l'initialiser immédiatement avec un champ final, et non à la première demande, cela supprimerait également le besoin de synchronisation.
synchronized ne doit être utilisé que si vous voulez que votre classe soit thread-safe. En fait, la plupart des classes ne devraient pas utiliser quand même synchronisé. La méthode synchronisée ne fournit qu'un verro sur cet objet et uniquement pour la durée de son exécution. si vous voulez vraiment sécuriser vos classes en thread, vous devriez envisager de rendre vos variables volatile ou synchroniser l'accès.
l’un des problèmes liés à l’utilisation de méthode synchronisée est que tous les membres de la classe utiliseraient le même verrouiller, ce qui ralentirait le déroulement du programme. Dans votre cas, la méthode synchronisée et le bloc ne seraient pas différents. Ce que je recommanderais est d'utiliser un verrouiller et d'utiliser un bloc synchronisé quelque chose comme ceci.
public class AClass {
private int x;
private final Object lock = new Object(); //it must be final!
public void setX() {
synchronized(lock) {
x++;
}
}
}
Parce que le verrouillage coûte cher, lorsque vous utilisez un blocage synchronisé, vous ne verrouillez que si _instance == null
, Et après _instance
, Vous aurez définitivement initialisé le verrouillage. Mais lorsque vous synchronisez sur une méthode, vous verrouillez inconditionnellement, même après l’initialisation de _instance
. C’est l’idée qui sous-tend le schéma d’optimisation du verrouillage revérifié http://en.wikipedia.org/wiki/Double-checked_locking .
Dans votre cas, les deux sont équivalents!
La synchronisation d'une méthode statique équivaut à un bloc synchronisé sur l'objet Class correspondant.
En fait, lorsque vous déclarez une méthode statique synchronisée, un verrou est obtenu sur le moniteur correspondant à l'objet Class.
public static synchronized int getCount() {
// ...
}
est identique à
public int getCount() {
synchronized (ClassName.class) {
// ...
}
}
Cela ne devrait pas être considéré comme une question d'utilisation optimale, mais cela dépend vraiment du cas d'utilisation ou du scénario.
méthodes synchronisées
Une méthode entière peut être marquée comme synchronisée, ce qui entraîne un verrou implicite sur la référence this (méthodes d'instance) ou sur la classe (méthodes statiques). C'est un mécanisme très pratique pour réaliser la synchronisation.
Steps Un thread accède à la méthode synchronisée. Il acquiert implicitement le verrou et exécute le code. Si un autre thread souhaite accéder à la méthode ci-dessus, il doit attendre. Le thread ne peut pas obtenir le verrou, sera bloqué et doit attendre que le verrou soit libéré.
Blocs Synchronisés
Pour obtenir un verrou sur un objet pour un ensemble spécifique de blocs de code, les blocs synchronisés conviennent le mieux. Comme un bloc suffit, utiliser une méthode synchronisée sera un gaspillage.
Plus spécifiquement avec le bloc synchronisé, il est possible de définir la référence d'objet sur laquelle veulent acquérir un verrou.