LinkedBlockingQueue est également implémenté à l'aide d'une liste chaînée unidirectionnelle. Il comporte également deux nœuds, qui sont utilisés respectivement pour stocker le premier et le dernier nœuds, et il existe également un nombre de variables atomiques avec une valeur initiale de 0, qui est utilisé pour enregistrer le nombre d'éléments de file d'attente. Il existe également deux instances de ReentrantLock, qui sont utilisées pour contrôler respectivement l'atomicité des éléments entrant et sortant de la file d'attente. Parmi elles, takeLock est utilisé pour contrôler qu'un seul thread peut obtenir des éléments de la tête de file d'attente en même temps, et que les autres threads doivent le faire. wait. PutLock contrôle qu'un seul thread peut obtenir des éléments de la tête de file d'attente en même temps. Un thread peut acquérir le verrou et ajouter des éléments à la fin de la file d'attente, et les autres threads doivent attendre. De plus, notEmpty et notFull sont des variables de condition. Elles disposent d'une file d'attente de conditions interne pour stocker les threads bloqués lors de l'entrée et de la sortie de la file d'attente. En fait, il s'agit du modèle producteur-consommateur. Voici le code pour créer un verrou exclusif.
private final AtomicInteger count = new AtomicInteger(); /** Lock held by take, poll, etc */ private final ReentrantLock takeLock = new ReentrantLock(); /** Wait queue for waiting takes */ private final Condition notEmpty = takeLock.newCondition(); /** Lock held by put, offer, etc */ private final ReentrantLock putLock = new ReentrantLock(); /** Wait queue for waiting puts */ private final Condition notFull = putLock.newCondition();
Lorsque le thread appelant effectue des opérations telles que prendre et interroger sur l'instance LinkedBlockingQueue, il doit obtenir le verrou takeLock pour garantir qu'un seul thread peut exploiter le nœud principal de la liste chaînée en même temps. De plus, étant donné que la maintenance de la file d'attente de conditions à l'intérieur de la variable de condition notEmpty utilise le mécanisme de gestion de l'état de verrouillage de takeLock, le thread appelant doit d'abord obtenir le verrou takeLock avant d'appeler les méthodes wait et signal de notEmpty, sinon une exception IllegalMonitorStateException sera levée. . NotEmpty maintient une file d'attente de conditions en interne Lorsque le thread acquiert le takeLock et appelle la méthode wait de notEmpty, le thread appelant sera bloqué, puis le thread sera placé dans la file d'attente de conditions à l'intérieur de notEmpty pour attendre qu'un thread appelle le signal notEmpty. méthode.
Lorsque vous effectuez des opérations put, offer et autres sur l'instance LinkedBlockingQueue, vous devez obtenir le verrou putLock pour vous assurer qu'un seul thread peut exploiter le nœud de queue de la liste chaînée en même temps. De même, puisque la maintenance de la file d'attente de conditions à l'intérieur de la variable de condition notFull utilise le mécanisme de gestion de l'état de verrouillage de putLock, le thread appelant doit d'abord acquérir le verrou putLock avant d'appeler les méthodes wait et signal de notFull, sinon une exception IllegalMonitorStateException sera levée. NotFull maintient une file d'attente de conditions en interne Lorsqu'un thread acquiert le verrou putLock et appelle la méthode wait de notFull, le thread appelant sera bloqué, puis le thread sera placé dans la file d'attente de conditions à l'intérieur de notFull pour attendre qu'un thread appelle notFull. méthode des signaux. Voici le code du constructeur sans paramètre de LinkedBlockingQueue.
Ce qui suit est le code de construction sans paramètre de LinkedBlockingQueue
public static final int MAX_VALUE = 0x7fffffff; public LinkedBlockingQueue() { this(Integer.MAX_VALUE); } public LinkedBlockingQueue(int capacity) { if (capacity <= 0) throw new IllegalAgrumentException(); this.capacity = capacity; last = head = new Node<E>(null); }
On peut voir à partir de ce code que la capacité de la file d'attente par défaut est 0x7fffffff, et les utilisateurs peuvent également spécifier eux-mêmes la capacité, donc dans une certaine mesure, cela peut être a déclaré que LinkedBlockingQueue est une file d'attente de blocage limitée.
offer operation
public boolean offer(E e) { //(1) if (e == null) throw new NullPointerException(); //(2) final AtomicInteger count = this.count; if (count.get() == capacity) return false; //(3) int c = -1; Node<E> node = new Node<E>(e); final ReentrantLock putLock = this.putLock; putLock.lock(); try { //(4) if (count.get() < capacity) { enqueue(node); c = count.getAndIncrement(); //(5) if (c + 1 < capacity) notFull.signal(); } } finally { //(6) putLock.unlock(); } //(7) if (c == 0) signalNotEmpty(); //(8) return c >= 0; }
Code (2) détermine si la file d'attente actuelle est pleine, supprime l'élément actuel et renvoie false
Code (3) obtient le verrou putLock Une fois que le thread actuel a obtenu le verrou, d'autres appels à. put et Le thread exploitant l'offre sera bloqué (le thread bloqué est placé dans la file d'attente de blocage AQS du verrou putLock).
Le code (4) réjuge ici si la file d'attente actuelle est pleine. En effet, d'autres threads peuvent avoir ajouté de nouveaux éléments à la file d'attente via des opérations put ou offer lors de l'exécution du code (2) et de l'acquisition du verrou putLock. . Si la file d'attente n'est effectivement pas pleine, de nouveaux éléments seront ajoutés à la file d'attente et le compteur sera incrémenté.
Le Code (5) détermine que s'il y a encore de l'espace libre dans la file d'attente après l'ajout du nouvel élément à la file d'attente, celui de la file d'attente conditionnelle qui réveille notFull est bloqué car l'opération d'attente de notFull est appelée (comme lorsque la méthode put est exécutée et que la file d'attente est pleine) Thread, parce que la file d'attente est maintenant inactive, un thread en file d'attente peut être réveillé à l'avance.
Le code (6) libère le verrou putLock acquis. Il convient de noter ici que le déverrouillage doit être effectué en final car même si le bloc try lève une exception, final sera exécuté. De plus, une fois le verrou libéré, l'un des autres threads bloqués par l'appel de l'opération put acquerra le verrou.
C0 dans le code (7) indique qu'il y a au moins un élément dans la file d'attente lors de l'exécution du code (6) pour libérer le verrou. S'il y a un élément dans la file d'attente, l'opération signalNotEmpty est exécutée.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!