Je crains de formuler des questions. J'ai le code morceau de (Java) suivante (pseudo):Gardez-vous une fermeture «évidente» ou utilisez-vous un verrouillage à double contrôle?
public SomeObject getObject(Identifier someIdentifier) {
// getUniqueIdentifier retrieves a singleton instance of the identifier object,
// to prevent two Identifiers that are equals() but not == (reference equals) in the system.
Identifier singletonInstance = getUniqueIdentifier(someIdentifier);
synchronized (singletonInstance) {
SomeObject cached = cache.get(singletonInstance);
if (cached != null) {
return cached;
} else {
SomeObject newInstance = createSomeObject(singletonInstance);
cache.put(singletonInstance, newInstance);
return newInstance;
}
}
}
Fondamentalement, il est 'unique' un identifiant (référence est égal, comme dans ==
), vérifie un cache, et dans le cas d'un défaut de cache , appelle une méthode coûteuse (impliquant l'appel d'une ressource externe et l'analyse, etc), met cela dans le cache, et retourne. Le Identifier
synchronisé, dans ce cas, évite deux equals()
mais pas ==
Identifier
objets utilisés pour appeler la méthode coûteuse, qui permettrait de récupérer la même ressource simultanément.
Les travaux ci-dessus. Je me demandais juste, et probablement micro-optimisant, une réécriture telle que la suivante qui utilise plus de récupération de cache naïf et double-vérifier le verrouillage soit «sûr» (sûr comme dans threadsafe, vide de conditions de course impair) et être 'plus optimal '(comme dans une réduction des verrous inutiles et des threads devant attendre un verrou)?
public SomeObject getObject(Identifier someIdentifier) {
// just check the cache, reference equality is not relevant just yet.
SomeObject cached = cache.get(someIdentifier);
if (cached != null) {
return cached;
}
Identifier singletonInstance = getUniqueIdentifier(someIdentifier);
synchronized (singletonInstance) {
// re-check the cache here, in case of a context switch in between the
// cache check and the opening of the synchronized block.
SomeObject cached = cache.get(singletonInstance);
if (cached != null) {
return cached;
} else {
SomeObject newInstance = createSomeObject(singletonInstance);
cache.put(singletonInstance, newInstance);
return newInstance;
}
}
}
On pourrait dire « juste le tester » ou « Il suffit de faire un micro-référence », mais de tester les bits multi-thread de code n'est pas mon point fort, et je doute que je serais capable de simuler situations réalistes ou fausses conditions de course avec précision. De plus, cela me prendrait une demi-journée, alors que l'écriture d'une question SO ne me prend que quelques minutes :).
Avertissement: [Le double-contrôle du verrouillage est interrompu en Java.] (Http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html) – cHao
cHao ce document décrive l'état de DCL avant le modèle de mémoire Java5. Sous le nouveau modèle de mémoire, la sémantique de volatile est fixée de sorte que le modèle DCL fonctionne correctement.Cependant, l'utilisation d'un util qui encapsule la paresse est toujours préférable: https://labs.atlassian.com/wiki/display/CONCURRENT/LazyReference+and+ResettableLazyReference –