2011-01-27 4 views
3

Je cherche un type de serrure où le fil qui tient le verrou peut le passer sur un autre fil de son choix.Serrure pouvant être transférée d'un fil à l'autre

Voici pourquoi je veux:

  • J'ai une classe similaire à ConcurrentHashMap - une collection spécialisée qui est divisée en plusieurs segments
  • La plupart des modifications nécessitent qu'un seul segment à verrouiller. Quelques-uns nécessitent le verrouillage de deux segments (en particulier, la modification d'une clé pour qu'elle passe d'un segment à un autre).
  • La plupart des lectures ne nécessitent pas de verrouillage: une lecture volatile est généralement suffisante, mais une recherche doit parfois verrouiller segments à la fois, si une vérification modification comptage ne
  • Recherche se fait dans plusieurs threads (via un ThreadPoolExecutor)
  • il est essentiel que la fonction de recherche a une vue cohérente de tous les segments (par exemple, il ne doit pas manquer une entrée alors qu'il est déplacé d'un segment à un autre.)
  • La tâche de recherche (pour un seul segment) peut être interrompue à tout moment.

Maintenant, je considère la situation où la méthode de recherche a été appelée dans le thread principal et découvre qu'il doit verrouiller tous les segments. Tous les verrous de segment doivent être tenus en même temps par le thread principal (pour s'assurer qu'il n'y a pas d'interférence) mais ce n'est pas le thread principal qui fera les mises à jour - c'est l'un des threads de travail. Par conséquent, j'essaie de faire passer le fil principal sur les verrous une fois qu'il sait qu'il a un cliché cohérent.

Nous avions l'habitude d'utiliser un seul verrou pour toute la collection, mais à mesure qu'il grossissait, il y avait trop de contention et une latence inacceptable pour les petites mises à jour. Déverrouiller et re-verrouiller (sur un ReentrantLock) n'est pas sûr - un autre thread peut modifier le segment avant que le thread de travail commence la recherche.

Un plaine Semaphore peut gérer le verrouillage et le déverrouillage par différents filetages. Le problème qui se pose alors est de savoir qui doit libérer le sémaphore - le thread de travail a besoin d'un moyen de signaler qu'il a pris possession du verrou (car il peut lancer une exception avant ou après ce point et le thread principal doit savoir s'il doit nettoyer up.) Il serait également difficile d'effectuer un test d'unité car on ne sait jamais si un sémaphore a été acquis ou publié dans le bon thread.

Ce serait un bonus si le verrou pourrait être utilisé rentrante dans d'autres méthodes (où il ne se transmet pas entre les threads).

J'imagine une combinaison d'un Semaphore et un AtomicBoolean ou AtomicReference est appelé, mais une recherche rapide sur google n'a révélé aucun exemple. Y a-t-il une raison pour laquelle cette approche ne devrait pas être utilisée?

+2

La concurrence fonctionne mieux avec un modèle simple. Compte tenu de la longueur de votre description et de la complexité de votre modèle, je vous suggère de simplifier vos exigences. IMHO est beaucoup plus susceptible de travailler efficacement. La seule méthode que je connaisse qui réveille un thread spécifique est Unsafe.park() et Unsafe.unpark (Thread) mais je ne recommanderais pas de les utiliser directement. –

+0

Si tout ce que vous voulez est un "verrou" qui peut être passé sur pourquoi ne pas utiliser un mécanisme de passage de jeton, avec chaque verrou de segment comme un jeton, avec lui étant littéralement transmis? Si le temps de recherche de la collection est assez long, ce jeton peut même être un fichier, le fichier agissant comme un verrou. Le thread principal peut simplement donner le "verrou" au thread de travail. – Fakrudeen

+0

Est-il possible de modéliser ceci de manière verrouillée en ayant des structures persistantes avec des "mises à jour" associatives créant de nouvelles structures plutôt que de les modifier en place? Votre description est une solution plutôt que le problème sous-jacent que vous résolvez, il est donc difficile d'en suggérer un. –

Répondre

2

Une utilisation inversée d'un verrou de lecture/écriture peut fonctionner. Dans cet idiome, les supports de verrou de lecture effectuent des écritures simultanées dans la structure de données (par exemple des créneaux indépendants dans un tableau). Le verrou d'écriture est utilisé pour acquérir un accès exclusif pour effectuer une lecture cohérente (par exemple additionner le tableau). C'est un idiome rarement utilisé, mais élégant dans ces cas bizarres. Votre problème est un peu difficile à comprendre, mais cela peut au moins offrir une source d'inspiration pour une solution concrète. Je suppose qu'avec une meilleure compréhension, le problème pourrait être simplifié de sorte qu'une solution classique soit plus appropriée.

+0

Comme je l'ai dit, lire la source de 'ConcurrentHashMap' peut le rendre plus clair. Ma classe est très similaire sauf que lorsqu'elle fait l'équivalent de 'contains (Object)' elle recherche chaque segment dans un thread (potentiellement) différent. Je vais voir si je peux régler la question pour la rendre plus claire. Je pense que votre solution fonctionnera bien. – finnw

+0

Je comprends les aspects techniques, mais pas les aspects comportementaux. Pourquoi une clé est-elle déplacée? Pourquoi devez-vous effectuer une recherche (par exemple, pourquoi ne pas utiliser un ConcurrentSkipListMap)? Quel est le contrat qui le rend différent d'une carte normale? –

+0

il n'y a pas de classement naturel des clés. Cette opération de recherche effectue une comparaison "floue" et doit vérifier chaque élément du segment à la recherche des quelques correspondances les plus proches. La modification d'une clé change occasionnellement son hashCode de sorte qu'il appartient à un segment différent. Je dois le déplacer de manière atomique pour m'assurer qu'il ne soit pas exclu d'une recherche pendant son déplacement. – finnw

Questions connexes