一些解析:
ArrayBlockingQueue用的锁一个Reentrantlock,而下面的两个notEmptyCondition和notFullCondition是通过Reentrantlock的newCondition()方法得到的。
/*** Main lock guarding all access */ |
072 | private final ReentrantLock lock; |
073 | /*** Condition for waiting takes */ |
074 | private final Condition notEmpty ; |
075 | /*** Condition for waiting puts */ |
076 | private final Condition notFull ; |
对于时间控制上即offer和poll都有阻塞的timeout时间控制,timeout时间控制是一个非常常用的功能,这里用的Condition的awaitNanos()方法,注意这个方法是阻塞性质的。
对于Offer的实现,大致思路是这样的,首先获取lock,然后由for循环控制进行插入,插入时首先判断是否已满,如果满了就进行awaitNanos方法进行阻塞等待直到插入成功或者超时。
public boolean offer(E e, long timeout, TimeUnit unit) |
271 | throws InterruptedException { |
272 |
273 | if (e == null ) throw new NullPointerException(); |
274 | long nanos = unit.toNanos(timeout); |
275 | final ReentrantLock lock = this .lock; |
276 | lock.lockInterruptibly(); |
277 | try { |
278 | for (;;) { |
279 | if (count != items.length) { |
280 | insert(e); |
281 | return true ; |
282 | } |
283 | if (nanos <= 0 ) |
284 | return false ; |
285 | try { |
286 | nanos = notFull.awaitNanos(nanos); |
287 | } catch (InterruptedException ie) { |
288 | notFull.signal(); // propagate to non-interrupted thread |
289 | throw ie; |
290 | } |
291 | } |
292 | } finally { |
293 | lock.unlock(); |
294 | } |
295 | } |
296 |
If the lock is held by another thread then the current thread becomes disabled for thread scheduling purposes and lies dormant until one of two things happens:
- The lock is acquired by the current thread; or
- Some other thread interrupts the current thread.
If the lock is acquired by the current thread then the lock hold count is set to one.
If the current thread:
- has its interrupted status set on entry to this method; or
- is interrupted while acquiring the lock,
InterruptedException
is thrown and the current thread's interrupted status is cleared.
提到关于时间控制,如果不能使用Reentrantlock及Condition的方式,还有其他什么方式么?如下这个使用Executor和FutureTask来进行时间控制,即通过使用将需要由时间控制的东东放到一个线程由executor执行,然后使用FutureTask的get方法来进行超时控制,如果超时了,就抛出一个timeoutException异常,也非常的好
- public boolean judgeMasters(final String hostName,final String accoutName){
- ExecutorService executor = Executors.newSingleThreadExecutor();
- FutureTask<Boolean> fuTask=new FutureTask<Boolean>(new Callable<Boolean>(){
- @Override
- public Boolean call() throws Exception {
- return exeCmd(hostName,accoutName);
- }
- });
- try {
- executor.execute(fuTask);
- return<span style="background-color: rgb(255, 102, 0);"> fuTask.get(5, TimeUnit.SECONDS);</span>
- } catch (InterruptedException e) {
- e.printStackTrace();
- return false;
- } catch (ExecutionException e) {
- e.printStackTrace();
- return false;
- } catch (TimeoutException e) {
- e.printStackTrace();
- return false;
- }
- }
关于Condition的官方说明,简单的说Condition的wait和singal方法就是为了替代原来的wait和notify,就像ReentrantLock或其他lock就像为了替换原来的synchronized关键字,并且像wait和notify的使用必须要被synchronized围绕,而condition也要被获得的lock围绕 :
Condition
factors out the Object
monitor methods (wait
,notify
andnotifyAll
) into distinct objects to give the effect of having multiple wait-sets per object, by combining them with the use of arbitraryLock
implementations. Where aLock
replaces the use ofsynchronized
methods and statements, aCondition
replaces the use of the Object monitor methods.
Conditions (also known as condition queues or condition variables) provide a means for one thread to suspend execution (to "wait") until notified by another thread that some state condition may now be true. Because access to this shared state information occurs in different threads, it must be protected, so a lock of some form is associated with the condition. The key property that waiting for a condition provides is that itatomically releases the associated lock and suspends the current thread, just likeObject.wait
.
A Condition
instance is intrinsically bound to a lock. To obtain aCondition
instance for a particularLock
instance use itsnewCondition()
method.
Note that Condition
instances are just normal objects and can themselves be used as the target in asynchronized
statement, and can have their own monitorwait
andnotification
methods invoked. Acquiring the monitor lock of aCondition
instance, or using its monitor methods, has no specified relationship with acquiring theLock
associated with thatCondition
or the use of its waiting andsignalling methods. It is recommended that to avoid confusion you never useCondition
instances in this way, except perhaps within their own implementation.
ArrayBlockingQueue源代码赏析:
- /**
- * Written by Doug Lea with assistance from members of JCP JSR-166
- * Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/licenses/publicdomain
- */
- package java.util.concurrent;
- import java.util.concurrent.locks.*;
- import java.util.*;
- // BEGIN android-note
- // removed link to collections framework docs
- // END android-note
- /***
- * A bounded {@linkplain BlockingQueue blocking queue} backed by an
- * array. This queue orders elements FIFO (first-in-first-out). The
- * <em>head</em> of the queue is that element that has been on the
- * queue the longest time. The <em>tail</em> of the queue is that
- * element that has been on the queue the shortest time. New elements
- * are inserted at the tail of the queue, and the queue retrieval
- * operations obtain elements at the head of the queue.
- *
- * <p>This is a classic "bounded buffer", in which a
- * fixed-sized array holds elements inserted by producers and
- * extracted by consumers. Once created, the capacity cannot be
- * increased. Attempts to <tt>put</tt> an element into a full queue
- * will result in the operation blocking; attempts to <tt>take</tt> an
- * element from an empty queue will similarly block.
- *
- * <p> This class supports an optional fairness policy for ordering
- * waiting producer and consumer threads. By default, this ordering
- * is not guaranteed. However, a queue constructed with fairness set
- * to <tt>true</tt> grants threads access in FIFO order. Fairness
- * generally decreases throughput but reduces variability and avoids
- * starvation.
- *
- * <p>This class and its iterator implement all of the
- * <em>optional</em> methods of the {@link Collection} and {@link
- * Iterator} interfaces.
- *
- * @since 1.5
- * @author Doug Lea
- * @param <E> the type of elements held in this collection
- */
- public class ArrayBlockingQueue<E> extends AbstractQueue<E>
- implements BlockingQueue<E>, java.io.Serializable {
- /***
- * Serialization ID. This class relies on default serialization
- * even for the items array, which is default-serialized, even if
- * it is empty. Otherwise it could not be declared final, which is
- * necessary here.
- */
- private static final long serialVersionUID = -817911632652898426L;
- /*** The queued items */
- private final E[] items;
- /*** items index for next take, poll or remove */
- private int takeIndex;
- /*** items index for next put, offer, or add. */
- private int putIndex;
- /*** Number of items in the queue */
- private int count;
- /**
- * Concurrency control uses the classic two-condition algorithm
- * found in any textbook.
- */
- /*** Main lock guarding all access */
- private final ReentrantLock lock;
- /*** Condition for waiting takes */
- private final Condition notEmpty;
- /*** Condition for waiting puts */
- private final Condition notFull;
- // Internal helper methods
- /***
- * Circularly increment i.
- */
- final int inc(int i) {
- return (++i == items.length)? 0 : i;
- }
- /***
- * Inserts element at current put position, advances, and signals.
- * Call only when holding lock.
- */
- private void insert(E x) {
- items[putIndex] = x;
- putIndex = inc(putIndex);
- ++count;
- notEmpty.signal();
- }
- /***
- * Extracts element at current take position, advances, and signals.
- * Call only when holding lock.
- */
- private E extract() {
- final E[] items = this.items;
- E x = items[takeIndex];
- items[takeIndex] = null;
- takeIndex = inc(takeIndex);
- --count;
- notFull.signal();
- return x;
- }
- /***
- * Utility for remove and iterator.remove: Delete item at position i.
- * Call only when holding lock.
- */
- void removeAt(int i) {
- final E[] items = this.items;
- // if removing front item, just advance
- if (i == takeIndex) {
- items[takeIndex] = null;
- takeIndex = inc(takeIndex);
- } else {
- // slide over all others up through putIndex.
- for (;;) {
- int nexti = inc(i);
- if (nexti != putIndex) {
- items[i] = items[nexti];
- i = nexti;
- } else {
- items[i] = null;
- putIndex = i;
- break;
- }
- }
- }
- --count;
- notFull.signal();
- }
- /***
- * Creates an <tt>ArrayBlockingQueue</tt> with the given (fixed)
- * capacity and default access policy.
- *
- * @param capacity the capacity of this queue
- * @throws IllegalArgumentException if <tt>capacity</tt> is less than 1
- */
- public ArrayBlockingQueue(int capacity) {
- this(capacity, false);
- }
- /***
- * Creates an <tt>ArrayBlockingQueue</tt> with the given (fixed)
- * capacity and the specified access policy.
- *
- * @param capacity the capacity of this queue
- * @param fair if <tt>true</tt> then queue accesses for threads blocked
- * on insertion or removal, are processed in FIFO order;
- * if <tt>false</tt> the access order is unspecified.
- * @throws IllegalArgumentException if <tt>capacity</tt> is less than 1
- */
- public ArrayBlockingQueue(int capacity, boolean fair) {
- if (capacity <= 0)
- throw new IllegalArgumentException();
- this.items = (E[]) new Object[capacity];
- lock = new ReentrantLock(fair);
- notEmpty = lock.newCondition();
- notFull = lock.newCondition();
- }
- /***
- * Creates an <tt>ArrayBlockingQueue</tt> with the given (fixed)
- * capacity, the specified access policy and initially containing the
- * elements of the given collection,
- * added in traversal order of the collection's iterator.
- *
- * @param capacity the capacity of this queue
- * @param fair if <tt>true</tt> then queue accesses for threads blocked
- * on insertion or removal, are processed in FIFO order;
- * if <tt>false</tt> the access order is unspecified.
- * @param c the collection of elements to initially contain
- * @throws IllegalArgumentException if <tt>capacity</tt> is less than
- * <tt>c.size()</tt>, or less than 1.
- * @throws NullPointerException if the specified collection or any
- * of its elements are null
- */
- public ArrayBlockingQueue(int capacity, boolean fair,
- Collection<? extends E> c) {
- this(capacity, fair);
- if (capacity < c.size())
- throw new IllegalArgumentException();
- for (Iterator<? extends E> it = c.iterator(); it.hasNext();)
- add(it.next());
- }
- /***
- * Inserts the specified element at the tail of this queue if it is
- * possible to do so immediately without exceeding the queue's capacity,
- * returning <tt>true</tt> upon success and throwing an
- * <tt>IllegalStateException</tt> if this queue is full.
- *
- * @param e the element to add
- * @return <tt>true</tt> (as specified by {@link Collection#add})
- * @throws IllegalStateException if this queue is full
- * @throws NullPointerException if the specified element is null
- */
- public boolean add(E e) {
- return super.add(e);
- }
- /***
- * Inserts the specified element at the tail of this queue if it is
- * possible to do so immediately without exceeding the queue's capacity,
- * returning <tt>true</tt> upon success and <tt>false</tt> if this queue
- * is full. This method is generally preferable to method {@link #add},
- * which can fail to insert an element only by throwing an exception.
- *
- * @throws NullPointerException if the specified element is null
- */
- public boolean offer(E e) {
- if (e == null) throw new NullPointerException();
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- if (count == items.length)
- return false;
- else {
- insert(e);
- return true;
- }
- } finally {
- lock.unlock();
- }
- }
- /***
- * Inserts the specified element at the tail of this queue, waiting
- * for space to become available if the queue is full.
- *
- * @throws InterruptedException {@inheritDoc}
- * @throws NullPointerException {@inheritDoc}
- */
- public void put(E e) throws InterruptedException {
- if (e == null) throw new NullPointerException();
- final E[] items = this.items;
- final ReentrantLock lock = this.lock;
- lock.lockInterruptibly();
- try {
- try {
- while (count == items.length)
- notFull.await();
- } catch (InterruptedException ie) {
- notFull.signal(); // propagate to non-interrupted thread
- throw ie;
- }
- insert(e);
- } finally {
- lock.unlock();
- }
- }
- /***
- * Inserts the specified element at the tail of this queue, waiting
- * up to the specified wait time for space to become available if
- * the queue is full.
- *
- * @throws InterruptedException {@inheritDoc}
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean offer(E e, long timeout, TimeUnit unit)
- throws InterruptedException {
- if (e == null) throw new NullPointerException();
- long nanos = unit.toNanos(timeout);
- final ReentrantLock lock = this.lock;
- lock.lockInterruptibly();
- try {
- for (;;) {
- if (count != items.length) {
- insert(e);
- return true;
- }
- if (nanos <= 0)
- return false;
- try {
- nanos = notFull.awaitNanos(nanos);
- } catch (InterruptedException ie) {
- notFull.signal(); // propagate to non-interrupted thread
- throw ie;
- }
- }
- } finally {
- lock.unlock();
- }
- }
- public E poll() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- if (count == 0)
- return null;
- E x = extract();
- return x;
- } finally {
- lock.unlock();
- }
- }
- public E take() throws InterruptedException {
- final ReentrantLock lock = this.lock;
- lock.lockInterruptibly();
- try {
- try {
- while (count == 0)
- notEmpty.await();
- } catch (InterruptedException ie) {
- notEmpty.signal(); // propagate to non-interrupted thread
- throw ie;
- }
- E x = extract();
- return x;
- } finally {
- lock.unlock();
- }
- }
- public E poll(long timeout, TimeUnit unit) throws InterruptedException {
- long nanos = unit.toNanos(timeout);
- final ReentrantLock lock = this.lock;
- lock.lockInterruptibly();
- try {
- for (;;) {
- if (count != 0) {
- E x = extract();
- return x;
- }
- if (nanos <= 0)
- return null;
- try {
- nanos = notEmpty.awaitNanos(nanos);
- } catch (InterruptedException ie) {
- notEmpty.signal(); // propagate to non-interrupted thread
- throw ie;
- }
- }
- } finally {
- lock.unlock();
- }
- }
- public E peek() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return (count == 0) ? null : items[takeIndex];
- } finally {
- lock.unlock();
- }
- }
- // this doc comment is overridden to remove the reference to collections
- // greater in size than Integer.MAX_VALUE
- /***
- * Returns the number of elements in this queue.
- *
- * @return the number of elements in this queue
- */
- public int size() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return count;
- } finally {
- lock.unlock();
- }
- }
- // this doc comment is a modified copy of the inherited doc comment,
- // without the reference to unlimited queues.
- /***
- * Returns the number of additional elements that this queue can ideally
- * (in the absence of memory or resource constraints) accept without
- * blocking. This is always equal to the initial capacity of this queue
- * less the current <tt>size</tt> of this queue.
- *
- * <p>Note that you <em>cannot</em> always tell if an attempt to insert
- * an element will succeed by inspecting <tt>remainingCapacity</tt>
- * because it may be the case that another thread is about to
- * insert or remove an element.
- */
- public int remainingCapacity() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return items.length - count;
- } finally {
- lock.unlock();
- }
- }
- /***
- * Removes a single instance of the specified element from this queue,
- * if it is present. More formally, removes an element <tt>e</tt> such
- * that <tt>o.equals(e)</tt>, if this queue contains one or more such
- * elements.
- * Returns <tt>true</tt> if this queue contained the specified element
- * (or equivalently, if this queue changed as a result of the call).
- *
- * @param o element to be removed from this queue, if present
- * @return <tt>true</tt> if this queue changed as a result of the call
- */
- public boolean remove(Object o) {
- if (o == null) return false;
- final E[] items = this.items;
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- int i = takeIndex;
- int k = 0;
- for (;;) {
- if (k++ >= count)
- return false;
- if (o.equals(items[i])) {
- removeAt(i);
- return true;
- }
- i = inc(i);
- }
- } finally {
- lock.unlock();
- }
- }
- /***
- * Returns <tt>true</tt> if this queue contains the specified element.
- * More formally, returns <tt>true</tt> if and only if this queue contains
- * at least one element <tt>e</tt> such that <tt>o.equals(e)</tt>.
- *
- * @param o object to be checked for containment in this queue
- * @return <tt>true</tt> if this queue contains the specified element
- */
- public boolean contains(Object o) {
- if (o == null) return false;
- final E[] items = this.items;
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- int i = takeIndex;
- int k = 0;
- while (k++ < count) {
- if (o.equals(items[i]))
- return true;
- i = inc(i);
- }
- return false;
- } finally {
- lock.unlock();
- }
- }
- /***
- * Returns an array containing all of the elements in this queue, in
- * proper sequence.
- *
- * <p>The returned array will be "safe" in that no references to it are
- * maintained by this queue. (In other words, this method must allocate
- * a new array). The caller is thus free to modify the returned array.
- *
- * <p>This method acts as bridge between array-based and collection-based
- * APIs.
- *
- * @return an array containing all of the elements in this queue
- */
- public Object[] toArray() {
- final E[] items = this.items;
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- Object[] a = new Object[count];
- int k = 0;
- int i = takeIndex;
- while (k < count) {
- a[k++] = items[i];
- i = inc(i);
- }
- return a;
- } finally {
- lock.unlock();
- }
- }
- /***
- * Returns an array containing all of the elements in this queue, in
- * proper sequence; the runtime type of the returned array is that of
- * the specified array. If the queue fits in the specified array, it
- * is returned therein. Otherwise, a new array is allocated with the
- * runtime type of the specified array and the size of this queue.
- *
- * <p>If this queue fits in the specified array with room to spare
- * (i.e., the array has more elements than this queue), the element in
- * the array immediately following the end of the queue is set to
- * <tt>null</tt>.
- *
- * <p>Like the {@link #toArray()} method, this method acts as bridge between
- * array-based and collection-based APIs. Further, this method allows
- * precise control over the runtime type of the output array, and may,
- * under certain circumstances, be used to save allocation costs.
- *
- * <p>Suppose <tt>x</tt> is a queue known to contain only strings.
- * The following code can be used to dump the queue into a newly
- * allocated array of <tt>String</tt>:
- *
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
- *
- * Note that <tt>toArray(new Object[0])</tt> is identical in function to
- * <tt>toArray()</tt>.
- *
- * @param a the array into which the elements of the queue are to
- * be stored, if it is big enough; otherwise, a new array of the
- * same runtime type is allocated for this purpose
- * @return an array containing all of the elements in this queue
- * @throws ArrayStoreException if the runtime type of the specified array
- * is not a supertype of the runtime type of every element in
- * this queue
- * @throws NullPointerException if the specified array is null
- */
- public <T> T[] toArray(T[] a) {
- final E[] items = this.items;
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- if (a.length < count)
- a = (T[])java.lang.reflect.Array.newInstance(
- a.getClass().getComponentType(),
- count
- );
- int k = 0;
- int i = takeIndex;
- while (k < count) {
- a[k++] = (T)items[i];
- i = inc(i);
- }
- if (a.length > count)
- a[count] = null;
- return a;
- } finally {
- lock.unlock();
- }
- }
- public String toString() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return super.toString();
- } finally {
- lock.unlock();
- }
- }
- /***
- * Atomically removes all of the elements from this queue.
- * The queue will be empty after this call returns.
- */
- public void clear() {
- final E[] items = this.items;
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- int i = takeIndex;
- int k = count;
- while (k-- > 0) {
- items[i] = null;
- i = inc(i);
- }
- count = 0;
- putIndex = 0;
- takeIndex = 0;
- notFull.signalAll();
- } finally {
- lock.unlock();
- }
- }
- /***
- * @throws UnsupportedOperationException {@inheritDoc}
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException {@inheritDoc}
- * @throws IllegalArgumentException {@inheritDoc}
- */
- public int drainTo(Collection<? super E> c) {
- if (c == null)
- throw new NullPointerException();
- if (c == this)
- throw new IllegalArgumentException();
- final E[] items = this.items;
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- int i = takeIndex;
- int n = 0;
- int max = count;
- while (n < max) {
- c.add(items[i]);
- items[i] = null;
- i = inc(i);
- ++n;
- }
- if (n > 0) {
- count = 0;
- putIndex = 0;
- takeIndex = 0;
- notFull.signalAll();
- }
- return n;
- } finally {
- lock.unlock();
- }
- }
- /***
- * @throws UnsupportedOperationException {@inheritDoc}
- * @throws ClassCastException {@inheritDoc}
- * @throws NullPointerException {@inheritDoc}
- * @throws IllegalArgumentException {@inheritDoc}
- */
- public int drainTo(Collection<? super E> c, int maxElements) {
- if (c == null)
- throw new NullPointerException();
- if (c == this)
- throw new IllegalArgumentException();
- if (maxElements <= 0)
- return 0;
- final E[] items = this.items;
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- int i = takeIndex;
- int n = 0;
- int sz = count;
- int max = (maxElements < count)? maxElements : count;
- while (n < max) {
- c.add(items[i]);
- items[i] = null;
- i = inc(i);
- ++n;
- }
- if (n > 0) {
- count -= n;
- takeIndex = i;
- notFull.signalAll();
- }
- return n;
- } finally {
- lock.unlock();
- }
- }
- /***
- * Returns an iterator over the elements in this queue in proper sequence.
- * The returned <tt>Iterator</tt> is a "weakly consistent" iterator that
- * will never throw {@link ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
- *
- * @return an iterator over the elements in this queue in proper sequence
- */
- public Iterator<E> iterator() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return new Itr();
- } finally {
- lock.unlock();
- }
- }
- /***
- * Iterator for ArrayBlockingQueue
- */
- private class Itr implements Iterator<E> {
- /***
- * Index of element to be returned by next,
- * or a negative number if no such.
- */
- private int nextIndex;
- /***
- * nextItem holds on to item fields because once we claim
- * that an element exists in hasNext(), we must return it in
- * the following next() call even if it was in the process of
- * being removed when hasNext() was called.
- */
- private E nextItem;
- /***
- * Index of element returned by most recent call to next.
- * Reset to -1 if this element is deleted by a call to remove.
- */
- private int lastRet;
- Itr() {
- lastRet = -1;
- if (count == 0)
- nextIndex = -1;
- else {
- nextIndex = takeIndex;
- nextItem = items[takeIndex];
- }
- }
- public boolean hasNext() {
- /**
- * No sync. We can return true by mistake here
- * only if this iterator passed across threads,
- * which we don't support anyway.
- */
- return nextIndex >= 0;
- }
- /***
- * Checks whether nextIndex is valid; if so setting nextItem.
- * Stops iterator when either hits putIndex or sees null item.
- */
- private void checkNext() {
- if (nextIndex == putIndex) {
- nextIndex = -1;
- nextItem = null;
- } else {
- nextItem = items[nextIndex];
- if (nextItem == null)
- nextIndex = -1;
- }
- }
- public E next() {
- final ReentrantLock lock = ArrayBlockingQueue.this.lock;
- lock.lock();
- try {
- if (nextIndex < 0)
- throw new NoSuchElementException();
- lastRet = nextIndex;
- E x = nextItem;
- nextIndex = inc(nextIndex);
- checkNext();
- return x;
- } finally {
- lock.unlock();
- }
- }
- public void remove() {
- final ReentrantLock lock = ArrayBlockingQueue.this.lock;
- lock.lock();
- try {
- int i = lastRet;
- if (i == -1)
- throw new IllegalStateException();
- lastRet = -1;
- int ti = takeIndex;
- removeAt(i);
- // back up cursor (reset to front if was first element)
- nextIndex = (i == ti) ? takeIndex : i;
- checkNext();
- } finally {
- lock.unlock();
- }
- }
- }
- }