11
votes

Comment et pourquoi un sémaphore peut-il donner plus de permis qu'il a été initialisé?

Je lis le livre Java Concurrence dans la pratique. Dans une section sur java.util.concurrent.semaphore , les lignes ci-dessous sont présentes dans le livre. C'est un commentaire sur sa mise en œuvre des objets "Permis virtuels"

La mise en œuvre n'a pas d'objets de permis réels et sémaphore fait ne pas associer les permis dispensés avec des threads, donc un permis acquis dans Un thread peut être libéré d'un autre fil. Vous pouvez penser à acquérir comme consommant un permis et libérer comme en créant un; une Semaphore ne se limite pas au nombre de permis qu'il a été créé avec.

Quelqu'un peut-il expliquer cela? J'ai du mal à comprendre cela. Si nous créons un pool de taille fixe, nous créons un nombre fixe de "permis". De la déclaration ci-dessus, on dirait que les "permis" peuvent continuer à grandir. Pourquoi est-il conçu de cette façon?


1 commentaires

ressemble à une phrase ornée


6 Réponses :


5
votes

Quelqu'un peut-il expliquer cela? De la déclaration ci-dessus, on dirait que les "permis" peuvent continuer à grandir.

Un sémaphore est un compteur de permis. Acquérir est comme la décrément qui attend plutôt que d'aller sous zéro. Il n'a pas de limite supérieure.

Pourquoi est-il conçu de cette façon?

parce que c'est simple à le faire.


5 commentaires

Aucune limite supérieure, la limite supérieure ne serait-elle pas le nombre de permis?


La limite supérieure est le nombre de fois que la libération des temps moins le nombre de fois acquis a été appelée. Vous pouvez appeler la sortie n'importe quel nombre de fois. Il y a une limite supérieure d'integer.max_value dans certaines implémentations.


Alors, comment va-t-on créer un sémaphore solide avec un maximum de 1 - quel que soit l'appel à la libération de fois?


@slott pour que je voudrais utiliser un atomicboolant. Vous pouvez SET (TRUE) Pour publier et CaqueAreAreSet (True, False) Pour acquérir. Ceci est non bloqué.


Est-ce trop exclu pour prolonger le sémaphore et la libération de remplacement pour faire des drainages ()?



11
votes

Au lieu de "distribuer" des objets de permis, la mise en œuvre a juste un compteur. Lorsqu'un nouveau permis est "créé", le compteur est augmenté, lorsqu'un permis est "retourné", le compteur est diminué.

Cela permet de mieux performer que de créer des objets réels tout le temps.

Le compromis est que le sémaphore lui-même ne peut pas détecter certains types d'erreurs de programmation (telles que des encaissements de permis non autorisés, ou des fuites de sémaphore). En tant que codeur, vous devez vous assurer de suivre les règles vous-même.


3 commentaires

Java fournit-il un type de sémaphore différent qui applique en réalité la limite de permis? Je veux juste que la libération () soit un non-op lorsque tous les permis ont déjà été libérés ... dans un système complexe avec beaucoup de threads, il n'est pas si facile de vous assurer que chaque thread acquiert et libère exactement un permis .


@DAssurant: Je suppose que vous pourriez rouler le vôtre au-dessus d'Atomicinteger. Mais cela ne ressemble pas à une bonne approche. Vous devez vraiment vous assurer que votre code de verrouillage est logiquement correct. Si vous trouvez la nécessité de faire publier un non-OP, cela signifie probablement que quelqu'un publie plus de serrures que ce qu'ils n'auraient dû le faire. Et cela brisera des choses qui ne peuvent pas être corrigées en ignorant simplement certains appels de sémaphore.


Vous pouvez également utiliser le mode de verrouillage traditionnel «synchronisé». Moins flexible, mais sûr (aucune fuite, prend soin de la réintensif, etc.).



5
votes

Je pense que cela signifie que cela signifie que ce que nous pouvons avoir besoin de sémaphore comme les moments où nous avons publié "extra" et plus les permis qu'il a créés.

tel que: xxx < P> À ce moment-là, ce sémaphore permet un permis à l'origine et un permis "extra"


1 commentaires

Je suis confondu par cela longtemps; et a l'air fou cela est autorisé



-1
votes

Peut-être la dernière ligne "Un sémaphore ne se limite pas au nombre de permis qu'il a été créé avec" est votre source de confusion.

Un sémaphore lors de la création est initialisé avec un ensemble de permis fixes. Cela devient alors le nombre maximum de permis que le sémaphore peut simultanément se passer à tout moment pendant la durée de vie de ce sémaphore. Vous ne peut pas augmenter de manière dynamique ce nombre, sauf en réinitialisant le sémaphore.

Le sens Si la ligne citée (de JCIP) est la suivante: Premièrement, la sémantique de la manière dont un sémaphore fonctionne ne se limite pas aux détails de la délivrance et de la reprise d'un permis - cela se manifeste dans le fait que tout fil peut-il Accès au sémaphore peut avoir un permis publié (même si ce fil ne possédait pas le permis de la première place)

Deuxièmement, vous pouvez réduire de manière dynamique les permis maximaux d'un sémaphore - en appelant VaporMits (int) méthode.


0 commentaires

4
votes

Comme mentionné dans le premier message " Semaphore ne se limite pas au nombre de permis qu'il a été créé avec "

Chaque appel à .Release () API augmentera le nombre de permis par un. Donc, les sémaphores n'ont pas de taille de permis fixe


1 commentaires

Pouvez-vous expliquer le travail .Release () alors? Donc, si je crée un objet de sémaphore avec 0, aucun thread ne peut acquérir le permis de celui-ci? Mais si j'appelle d'abord .Release () d'abord () .acquire () dans la ligne suivante, cela permettra à un fil d'acquérir un permis?



0
votes

Il est surprenant à certains d'entre nous.

Vous pouvez facilement sous-classer un sémaphore délimitée. P>

/**
 * Terrible performance bounded semaphore.
 **/
 public class BoundedSemaphore extends Semaphore {
    private static final long serialVersionUID = -570124236163243243L;
    final int bound;
    public BoundedSemaphore(int permits) {
        super(permits);
        bound=permits;
    }

    @Override
    synchronized public void acquire() throws InterruptedException {
        super.acquire();
    }
    
    @Override
    synchronized public boolean tryAcquire() {
        return super.tryAcquire();
    }
    
    @Override
    synchronized public void release() {
        if( availablePermits()<bound){ 
            super.release();
        }
    }
    @Override
    synchronized public void acquire(int count) throws InterruptedException {
        super.acquire(count);
    }
    
    @Override
    synchronized public boolean tryAcquire(int count) {
        return super.tryAcquire(count);
    }
    
    @Override
    synchronized public void release(int count) {
        if( availablePermits()<bound){ 
            super.release(bound-availablePermits());
        }
    }
}


0 commentaires