2011-11-02 3 views
0

J'ai un objet Utilisateur avec deux verrous, inventoryLock et currencyLock. Souvent, ces verrous seront utilisés individuellement, par ex.Comment éviter une impasse facile?

synchronized (user.inventoryLock) { 

// swap items 
tmp = user.inventory[x]; 
user.inventory[x] = user.inventory[y]; 
user.inventory[y] = tmp; 

} 

ou

synchronized (user.currencyLock) { 

if (user.money < loss) throw new Exception(); 
user.money -= loss; 

} 

Mais parfois, un morceau de code nécessite les deux serrures:

synchronized (user.currencyLock) { 
synchronized (user.inventoryLock) { 

    if (user.money < item.price) throw new Exception(); 
    user.money -= item.price; 
    user.inventory[empty] = item; 

} 
} 

Cela semble simple, mais il y a plus de bits de code en utilisant les deux verrous que juste cet exemple, et je sais par expérience que si plusieurs morceaux de code requièrent les mêmes verrous partagés, ils risquent d'être bloqués.

Quel est un bon moyen d'éviter cela?

Est-ce qu'il y a peut-être un mécanisme qui me permet de verrouiller atomiquement deux objets?

+0

Votre code montre un sérieux manque d'encapsulation. Vous accédez directement à l'état de l'utilisateur, sans passer par les méthodes. L'état doit être encapsulé dans la classe, ce qui devrait centraliser la synchronisation et s'assurer que toute la synchronisation nécessaire est faite. Vous forcez chaque client de votre utilisateur à se synchroniser correctement, plutôt que de fournir ce service dans la classe User elle-même. –

+0

@JBNizet Ceci est juste un exemple de code. Le code réel implique, entre autres, des appels de base de données que la classe d'utilisateurs n'est pas capable d'effectuer. –

Répondre

0

Juste après avoir posté cette question, j'ai trouvé moi-même une solution simple: Assurez-vous que tout le code acquiert les serrures dans le même ordre. De cette façon, il ne peut jamais y avoir deux threads en tenant l'un d'entre eux.

S'il n'y a pas d'ordre naturel, l'alphabétique suffirait et serait facile à retenir.

3

toujours verrouiller un verrou avant l'autre, l'une des exigences d'une impasse est un modèle d'attente circulaire

par exemple, si vous pouvez vous assurer que vous serez toujours verrouiller currencyLock avant de verrouiller inventoryLock et ne jamais tenter de verrouiller currencyLock quand vous avez déjà inventoryLock vous serez bien

+0

C'est ce que j'ai trouvé aussi, donc je ne peux pas m'empêcher d'être d'accord :) –

+0

Vous ne pouvez pas vous empêcher de verrouiller l'un avant l'autre. Vous devez toujours * acquérir les verrous dans le même ordre. * – EJP

0

Je ne vois aucune possibilité circular wait ici, ce qui est l'une des 4 conditions nécessaires pour un blocage.

+0

Il y a plus de morceaux de code qui nécessitent les deux verrous, pas seulement l'exemple que j'ai posté. Je vais clarifier cela. –