65 votes

Java Simple nom de la base de serrures?

MySQL dispose d'une fonction très pratique:

SELECT GET_LOCK("SomeName")

Ceci peut être utilisé pour créer simple, mais très spécifique, à partir du nom des verrous pour une application. Cependant, elle nécessite une connexion de base de données.

J'ai beaucoup de situations comme:

someMethod() {
    // do stuff to user A for their data for feature X
}

Il n'est pas judicieux de simplement synchroniser cette méthode, parce que, par exemple, si cette méthode est appelée pour l'utilisateur B dans l'intervalle, l'utilisateur B n'a pas besoin d'attendre pour Un utilisateur de se terminer avant qu'il ne commence, seules les opérations de l'utilisateur et la fonctionnalité X combinaison besoin d'attendre.

Avec MySql lock je pourrais faire quelque chose comme:

someMethod() {
    executeQuery("SELECT GET_LOCK('userA-featureX')")
    // only locked for user A for their data for feature X
    executeQuery("SELECT RELEASE_LOCK('userA-featureX')")
}

Depuis Java de verrouillage est basé sur des objets, il me semble que j'aurais besoin de créer un nouvel objet à représenter la situation de cette serrure, puis le mettre dans un statique de cache quelque part donc tous les threads peuvent le voir. Des demandes ultérieures de verrouillage pour que la situation serait alors de localiser le verrou de l'objet dans le cache et l'acquisition de son verrouillage. J'ai essayé de créer quelque chose comme ça, mais alors le verrou du cache lui-même a besoin de synchronisation. Aussi, il est difficile de détecter si un verrou de l'objet n'est plus utilisé de sorte qu'il peut être retiré à partir du cache.

J'ai regardé le Java simultanées paquets, mais rien ne se démarque comme étant capable de gérer quelque chose comme ça. Est-il un moyen facile à mettre en œuvre, ou suis-je en regardant ce de la mauvaise perspective?

Edit:

Pour être clair, je ne cherche pas à créer un prédéfini piscine de serrures à l'avance, je voudrais créer sur la demande. Certains pseudo-code pour ce que je pense est:

LockManager.acquireLock(String name) {
    Lock lock;  

    synchronized (map) {
        lock = map.get(name);

        // doesn't exist yet - create and store
        if(lock == null) {
            lock = new Lock();
            map.put(name, lock);
        }
    }

    lock.lock();
}

LockManager.releaseLock(String name) {
    // unlock
    // if this was the last hold on the lock, remove it from the cache
}

36voto

dmoebius Points 394

Toutes ces réponses je vois sont trop compliqués. Pourquoi ne pas simplement utiliser:

public void executeInNamedLock(String lockName, Runnable runnable) {
  synchronized(lockName.intern()) {
    runnable.run();
  }
}

Le point clé est la méthode intern: il s'assure que la Chaîne de caractères retournée est d'un unique objet, et donc il peut être utilisé en tant que machine virtuelle-exemple à l'échelle de mutex. Tous les internés les Chaînes sont tenues dans un pool mondial, de sorte que votre statique de cache vous parliez dans votre question initiale. Ne vous inquiétez pas à propos de memleaks; ces chaînes seront gc ed si aucun autre thread référence. Notez cependant que, jusqu'à et y compris Java6 cette piscine est gardé dans PermGen space au lieu de le tas, alors vous pourriez avoir à augmenter.

Il y a un problème, par contre si un autre code dans votre vm verrous sur la même chaîne pour des raisons complètement différentes, mais une) c'est très peu probable, et b) vous pouvez contourner ce problème en introduisant des espaces de noms, par exemple, executeInNamedLock(this.getClass().getName() + "_" + myLockName);

21voto

sjr Points 5686

Pouvez-vous avoir un Map<String, java.util.concurrent.Lock>? Chaque fois que vous avez besoin d'une serrure, vous appelez map.get(lockName).lock().

Voici un exemple d'utilisation de Google Goyave:

Map<String, Lock> lockMap = new MapMaker().makeComputingMap(new Function<String, Lock>() {
  @Override public Lock apply(String input) {
    return new ReentrantLock();
  }
});

Ensuite, lockMap.get("anyOldString") sera la cause d'une nouvelle serrure à être créé si nécessaire et vous retourner. Vous pouvez ensuite appeler lock() sur le verrou. makeComputingMap retourne une Carte qui est thread-safe, vous pouvez simplement partager avec tous vos fils.

20voto

irreputable Points 25577
// pool of names that are being locked
HashSet<String> pool = new HashSet<String>(); 

lock(name)
    synchronized(pool)
        while(pool.contains(name)) // already being locked
            pool.wait();           // wait for release
        pool.add(name);            // I lock it

unlock(name)
    synchronized(pool)
        pool.remove(name);
        pool.notifyAll();

18voto

moj Points 181

c'est peut-être utile pour vous: jkeylockmanager

Edit:

Ma première réaction a été sans doute un peu court. Je suis l'auteur et a été confronté à ce problème plusieurs fois et ne pouvait pas trouver une solution existante. C'est pourquoi j'ai fait cette petite bibliothèque sur Google Code.

6voto

McDowell Points 62645

Pour le verrouillage de quelque chose comme un nom d'utilisateur, en mémoire Locks dans une carte peut-être un peu baveur. Comme alternative, vous pouvez rechercher à l'aide de WeakReferences avec WeakHashMap pour créer des objets mutex qui peuvent être des ordures collectées lorsque rien n'y fait référence. Cela vous évite de faire n'importe quel manuel de référence de comptage pour libérer de la mémoire.

Vous pouvez trouver une mise en œuvre ici. Notez que si vous faites fréquemment des recherches sur la carte, vous risquez de rencontrer les problèmes de conflit d'acquérir le mutex.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X