В предыдущей главе мы объяснили очередь блокировки, реализованную в Arrayblockingqueue, используя форму массива.
Длина массива должна быть определена при создании. Если длина массива мала, очередь ArrayBlockingQueue будет легко заблокирована. Если длина массива велика, он легко тратит память.
Структура данных очереди естественным образом подходит для формы связанных списков, а LinkedBlockingqueue - это блокирующая очередь, реализованная с использованием связанных списков.
1. Реализация связанного списка
1.1 Узел внутренний класс
/ *** Узел связанного списка также используется для реализации одностороннего связанного списка*/ Статический узел класса <e> {e item; // укажите на следующий узел связанного списка узла <e> Далее; Узел (e x) {item = x; }}Существует переменная E, которая хранит данные, и следующая переменная, указывающая на следующий узел. Он может реализовать самый простой список одностороннего.
1.2 Как реализовать список ссылок
/ *** Следующая точка к головке очереди, и этот узел не хранит данные*/ переходной узел <e> Head; / *** Хвост
Чтобы реализовать связанный список, должно быть две переменные, одна представляет собой главу связанного списка, а другая представляет собой последний из связанного списка. Как голова, так и последняя инициализируются, когда создается объект LinkedBlockingQueue.
последний = head = новый узел <e> (null);
Обратите внимание, что здесь используется небольшой трюк. Головой узел связанного списка не хранит данные. Следующий узел, который он указывает на действительно сохранить первые данные в связанном списке. Последний в конце связанного списка действительно хранит последние данные в связанном списке.
1.3 Вставьте и удаляйте узлы
/ *** Вставьте узел в хвост очереди*/ private void enqueue (node <e> node) {// assert putlock.isheldbycurrentthread (); // Текущий поток должен был получить блокировку путлока // точка следующего эталона исходного хвостового узла очереди на новый узел узла, а затем установил узел узла на хвостовой узел очереди в последний раз // Это осознает вставку узла в хвост очереди }Очень просто вставить узел в конце связанного списка. Укажите следующий узел исходной очереди в последнюю очередь на новый узел узла, а затем назначьте новый узел узла к последнему узлу в конце очереди. Это позволяет вставить новый узел.
// Удалить узел головки очереди и вернуть удаленные данные узла private e dequeue () {// assert takelock.isheldbycurrentthread (); // текущий поток должен был получить блокировку Takelock // assert head.item == null; Узел <e> h = голова; // Данные первого элемента в очереди хранятся в первом узле узла <e> First = h.Next; H.Next = H; // Помогите GC // Установка нового значения головы эквивалентно удалению первого узла. Потому что сам узел головы не хранит данные Data Head = First; // данные заголовка очереди e x = first.item; // Удалить исходные данные первым.item = null; возврат x; }Обратите внимание, что Head не является заголовком, его следующей точкой к заголовку, поэтому очень просто удалить заголовок, который должен назначить Head.next для головы, а затем вернуть данные исходного узла Head.next.
При удалении обратите внимание на ситуацию, когда связанный список пуст. Значение head.next добавляется с использованием метода Enqueue. Когда Head.next == Последнее это означает, что последний элемент был удален. Когда Head.next == Null, его нельзя удалить, потому что связанный список уже пуст. Здесь нет никакого решения, потому что решение было вынесено в том месте, где называется метод Dequeue.
2. Синхронная блокировка повторного блокировки и условия
Поскольку очередь блокировки должна быть заблокирована и ждать, когда очередь пустая, а очередь заполнена, тогда требуются два условия. Чтобы обеспечить безопасность многопоточного параллелизма, необходим блокировка синхронизации. Это было сказано в ArrayBlockingQueue.
Здесь мы поговорим о разных вещах о LinkedBlockingQueue.
/ ** Эксклюзивная блокировка, используемая для решения проблем параллелистики для вставки операций в очереди, то есть, положить и предлагать операции*/ Частный окончательный результат повторного замены putlock = new Reentrantlock (); / ** Условие условия, в котором очередь не удовлетворена, она генерируется путлок -блокировкой*/ частное окончательное условие natfull = putlock.newcondition (); / ** Эксклюзивная блокировка, используемая для решения проблем с параллелизмом по удалению операций с очередью, то есть операциями «Возьми и опросы»*/ Private Final Reentrantlock takelock = new Reentrantlock (); / ** Условие условия, что очередь не является пустой, она использует частное окончательное условие, сгенерированное блокировкой Takelock*/ Private Final Condity notempty = takelock.newCondition ();
2.1 Putlock и Takelock
Мы нашли два блокировки:
Согласно вышеуказанному утверждению, могут быть операции вставки и удаления элементов одновременно, так что не будет никаких проблем?
Давайте подробно проанализируем это. Для очередей операции разделены на три типа:
Поэтому используйте Putlock Lock, чтобы сохранить последнюю переменную в безопасности, и используйте блокировку Takelock, чтобы сохранить переменную головки в безопасности.
Для всех переменных подсчета, связанных с количеством элементов в очереди, Atomicinteger используется для обеспечения вопросов безопасности параллелистики.
/ ** Количество элементов в очереди, используйте здесь переменную AtomicInteger, чтобы обеспечить вопросы безопасности параллелизма*/ Частный конечный AtomicInteger Count = new Atomicinteger ();
2.2 Примечательно и нотмпти
2.3 Процесс управления
При вставке элемента:
При удалении элементов:
Также обратите внимание, что сигнал и ожидание методов состояния должны быть вызваны при получении блокировки. Следовательно, существуют сигнальные и сигнальные методы:
/*** Проснитесь в поток, ожидая в условиях Notempty, то есть при удалении заголовка очереди, поток, который, как оказалось пустым, и вынуждено подождать. * Обратите внимание, что, поскольку для вызова сигнального метода условия необходимо получить соответствующую блокировку, здесь называется метод takelock.lock (). * Когда элемент вставлен в очередь (т.е. поместить или предложить операцию), очередь определенно не пуста, и этот метод будет вызван. */ private void signalnotempty () {final Reentrantlock takelock = this.takelock; takelock.lock (); try {notempty.signal (); } наконец {takelock.unlock (); }} /*** Разбудите поток, ожидающую в заметном условии, то есть, когда элемент добавляется в конце очереди, поток, который заполнен и вынужден ждать. * Обратите внимание, что, поскольку для вызова сигнального метода условия необходимо получить соответствующую блокировку, поэтому метод putlock.lock () вызывается здесь* Когда элемент (то есть операция принятия или опроса) удаляется в очередь, очередь определенно будет неудовлетворен и вызовет этот метод. */ private void SignalNotfull () {окончательный reenterlock putlock = this.putlock; putlock.lock (); try {natfull.signal (); } наконец {putlock.unlock (); }}3. Вставьте метод элемента
public void put (e e) бросает прерванные экзаливы {if (e == null) бросить новый NullPointerException (); // Записать количество элементов перед операцией вставки int c = -1; // Создать новый узел узла <e> node = new Node <e> (e); Окончательный reentrantlock putlock = this.putlock; окончательный atomicinteger count = this.count; putlock.lockEntertaill (); Попробуйте {// Укажите, что очередь заполнена, тогда вам нужно вызвать метод natfull.await, чтобы текущий поток ожидал, пока (count.get () == емкость) {natufull.await (); } // вставить новую энкеуээ элемента (узел); // Добавить 1 к количеству элементов в текущей очереди и вернуть количество элементов, прежде чем добавить 1. c = count.getandIncrement (); // C + 1 <емкость означает, что очередь не заполнена, а поток, который может ждать, чтобы операция вставки пробуждается, если (C + 1 <емкость) with) witch) natfull.signal (); } наконец {putlock.unlock (); } // c == 0 означает, что очередь пуст перед вставкой. Когда очередь пуст к размещению элемента, // разбудить поток в ожидании удаления // предотвратить частое сборы блокировки Takelock, потреблять производительность, если (c == 0) SignalNotempty (); }Принимая метод POT в качестве примера, общий процесс такой же, как и мы введены ранее. Вот очень странный код. Когда элемент вставлен, если вы обнаружите, что очередь не заполнена, то вызовите natfull.signal (), чтобы разбудить поток в ожидании вставки.
Все очень смущены. Вообще говоря, этот метод должен быть помещен в элемент Delete (метод серии), потому что, когда мы удаляем элемент, очередь должна быть недостаточно, а затем вызовите метод natfull.signal (), чтобы разбудить поток, ожидая вставки.
Это в основном делается здесь, потому что при вызове метода сигнала соответствующий блокировка должна быть получена сначала. Замок, используемый в методе серии Take, является Takelock. Если вы хотите вызвать метод natfull.signal (), вы должны сначала получить блокировку Putlock. Это приведет к снижению производительности, поэтому используется другой метод.
4. Удалить элемент заголовка очереди
public e take () бросает прерванную экспрессию {e x; int c = -1; окончательный atomicinteger count = this.count; окончательный reentrantlock takelock = this.takelock; takelock.lockEntertaill (); Попробуйте {// означает, что очередь пуста, тогда вам нужно вызвать метод notempty.await, чтобы текущий поток ожидал, как (count.get () == 0) {notempty.await (); } // Удалить элемент заголовка очереди и вернуть его x = dequeue (); // Возврат номера текущих очередей, а затем вычтите количество очередей одним c = count.getandDecrement (); // C> 1 означает, что очередь не является пустой, поэтому она разбудит поток, который может ждать операции удаления, если (c> 1) notempty.signal (); } наконец {takelock.unlock (); } /*** C == емкость означает, что очередь заполнена перед операцией удаления. * Разбудить поток в ожидании вставки только тогда, когда элемент удаляется из полной очереди* предотвращает частое получение блокировки путлока, потребляя производительность*/ if (c == емкость) Signalnotfull (); возврат x; }Почему называется метод notempty.signal (), сравните наше объяснение в методе вставки.
5. Посмотреть элементы заголовка очереди
// Просмотреть элемент заголовка очереди public e peek () {// очередь пуста, вернуть null if (count.get () == 0) return null; окончательный reentrantlock takelock = this.takelock; takelock.lock (); try {// Получить узл заголовка очереди первого узла <e> first = head.next; // First == null означает, что очередь пуста, вернуть null if (first == null) return null; else // вернуть элемент заголовка очереди return.item; } наконец {takelock.unlock (); }}Посмотреть элемент заголовка очереди, который включает в себя узел головного узла, поэтому необходимо использовать блокировку Takelock.
VI Другие важные методы
6.1 Сними (объект o) Метод
// Удалить указанный элемент из очереди o public boolean remove (Object o) {if (o == null) вернуть false; // Поскольку он не должен удалять элемент заголовка списка, участвуют две переменные и последнее, // Putlock и Takelock должны быть заблокированы полностью блокировкой (); try {// пройти всю очередь, P представляет текущий узел, а Trail представляет предыдущий узел текущего узла //, поскольку он представляет собой односторонний связанный список, необходимо записывать два узла для (Node <e> Trail = Head, p = trail.next; p! (o.equals (p.item)) {unlink (p, trail); вернуть истину; }} вернуть false; } Наконец {FullUnunlock (); }}Удалить указанный элемент из списка. Поскольку удаленный элемент не обязательно находится в голове списка, может быть две переменные и последнее, поэтому вы должны использовать блокировки Putlock и Takelock одновременно. Поскольку это односторонний связанный список, для записи предыдущего узла необходима вспомогательная переменная тропа, чтобы можно было удалить текущий узел P.
6.2 Метод Unlink (Node <e> p, Node <e> Trail)
// Удалить текущий узел P, и след представляет предыдущий узел P void unlink (node <e> p, node <e> trail) {// Установить данные текущего узла в null p.item = null; // это удаляет узел P Trail.next = P.Next; // Если узел P является последним последним из очереди, и он удален, затем установите след на последнее, если (последний == P) последний = TRAIL; // Вычитайте количество элементов одним, если исходная очередь заполнена, то вызовите метод natfull.signal () // Фактически, это вызывается непосредственно без суждения, поскольку блокировка путлок должна быть получена здесь, если (count.getandDecrement () == емкость) withip) natufull.signal (); }Чтобы удалить узел P в связанном списке, вам нужно только указать на следующую из предыдущего узла P на следующий узел следующего узла p.
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.