summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Wilson <jessewilson@google.com>2009-07-28 17:34:57 -0700
committerJesse Wilson <jessewilson@google.com>2009-07-28 17:35:12 -0700
commitbba8d1acd6dfff06c94d761c67a30154ca5ca5df (patch)
treef68043d73ad474a98471ed8c4081b3ffa274c8f6
parentb5d658bb3d92e590f2320058064b807c4034860c (diff)
downloadlibcore-bba8d1acd6dfff06c94d761c67a30154ca5ca5df.zip
libcore-bba8d1acd6dfff06c94d761c67a30154ca5ca5df.tar.gz
libcore-bba8d1acd6dfff06c94d761c67a30154ca5ca5df.tar.bz2
Update concurrent module to Harmony r798021.
commit 56bcdc2a3b881883409267c3bd16294d80716859 Merge: 903691a f429dca Author: Jesse Wilson <jessewilson@google.com> Date: Mon Jul 27 17:33:19 2009 -0700 Merge branch 'concurrent_798021' into concurrent_dalvik Conflicts: libcore/concurrent/.classpath libcore/concurrent/build.xml libcore/concurrent/make/run-test.xml libcore/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java libcore/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentMap.java libcore/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java libcore/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java libcore/concurrent/src/main/java/java/util/concurrent/DelayQueue.java libcore/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java libcore/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java libcore/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java libcore/concurrent/src/main/java/java/util/concurrent/Semaphore.java libcore/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java libcore/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java libcore/concurrent/src/main/java/java/util/concurrent/TimeUnit.java libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java libcore/concurrent/src/main/java/java/util/concurrent/atomic/package.html libcore/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java libcore/concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java libcore/concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java libcore/concurrent/src/main/java/java/util/concurrent/locks/package.html libcore/concurrent/src/main/java/java/util/concurrent/package.html libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongFieldUpdaterTest.java libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceFieldUpdaterTest.java libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceTest.java libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ConcurrentHashMapTest.java libcore/concurrent/src/test/java/tests/api/java/util/concurrent/DelayQueueTest.java libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorsTest.java libcore/concurrent/src/test/java/tests/api/java/util/concurrent/JSR166TestCase.java libcore/concurrent/src/test/java/tests/api/java/util/concurrent/LinkedBlockingQueueTest.java libcore/concurrent/src/test/java/tests/api/java/util/concurrent/LockSupportTest.java libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantLockTest.java libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantReadWriteLockTest.java commit 903691ae71cff640d5487a3d34a20e8767dbfb66 Author: Jesse Wilson <jessewilson@google.com> Date: Mon Jul 27 16:09:58 2009 -0700 Dalvik Concurrent commit f429dca21c408ee62e688f60d5e110718374e944 Author: Jesse Wilson <jessewilson@google.com> Date: Mon Jul 27 16:08:25 2009 -0700 Concurrent 798021 commit b2c76fdd1056113000140bc4af57300c87469d2d Author: Jesse Wilson <jessewilson@google.com> Date: Mon Jul 27 16:03:45 2009 -0700 Concurrent 527399
-rw-r--r--concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java71
-rw-r--r--concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java341
-rw-r--r--concurrent/src/main/java/java/util/concurrent/BlockingQueue.java289
-rw-r--r--concurrent/src/main/java/java/util/concurrent/CompletionService.java53
-rw-r--r--concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java1015
-rw-r--r--concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java466
-rw-r--r--concurrent/src/main/java/java/util/concurrent/ConcurrentMap.java154
-rw-r--r--concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java1933
-rw-r--r--concurrent/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java340
-rw-r--r--concurrent/src/main/java/java/util/concurrent/CountDownLatch.java161
-rw-r--r--concurrent/src/main/java/java/util/concurrent/CyclicBarrier.java316
-rw-r--r--concurrent/src/main/java/java/util/concurrent/DelayQueue.java381
-rw-r--r--concurrent/src/main/java/java/util/concurrent/Delayed.java12
-rw-r--r--concurrent/src/main/java/java/util/concurrent/Exchanger.java677
-rw-r--r--concurrent/src/main/java/java/util/concurrent/ExecutionException.java8
-rw-r--r--concurrent/src/main/java/java/util/concurrent/Executor.java9
-rw-r--r--concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java143
-rw-r--r--concurrent/src/main/java/java/util/concurrent/ExecutorService.java259
-rw-r--r--concurrent/src/main/java/java/util/concurrent/Executors.java295
-rw-r--r--concurrent/src/main/java/java/util/concurrent/Future.java37
-rw-r--r--concurrent/src/main/java/java/util/concurrent/FutureTask.java147
-rw-r--r--concurrent/src/main/java/java/util/concurrent/Java6Arrays.java77
-rw-r--r--concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java203
-rw-r--r--concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java371
-rw-r--r--concurrent/src/main/java/java/util/concurrent/RejectedExecutionException.java2
-rw-r--r--concurrent/src/main/java/java/util/concurrent/RejectedExecutionHandler.java16
-rw-r--r--concurrent/src/main/java/java/util/concurrent/ScheduledExecutorService.java108
-rw-r--r--concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java1113
-rw-r--r--concurrent/src/main/java/java/util/concurrent/Semaphore.java376
-rw-r--r--concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java1193
-rw-r--r--concurrent/src/main/java/java/util/concurrent/ThreadFactory.java13
-rw-r--r--concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java1856
-rw-r--r--concurrent/src/main/java/java/util/concurrent/TimeUnit.java205
-rw-r--r--concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java31
-rw-r--r--concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java75
-rw-r--r--concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java73
-rw-r--r--concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java187
-rw-r--r--concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java103
-rw-r--r--concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java79
-rw-r--r--concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java232
-rw-r--r--concurrent/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java36
-rw-r--r--concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java38
-rw-r--r--concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java40
-rw-r--r--concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java180
-rw-r--r--concurrent/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java38
-rw-r--r--concurrent/src/main/java/java/util/concurrent/atomic/package-info.java163
-rw-r--r--concurrent/src/main/java/java/util/concurrent/atomic/package.html132
-rw-r--r--concurrent/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java56
-rw-r--r--concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java1427
-rw-r--r--concurrent/src/main/java/java/util/concurrent/locks/Condition.java294
-rw-r--r--concurrent/src/main/java/java/util/concurrent/locks/Lock.java250
-rw-r--r--concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java163
-rw-r--r--concurrent/src/main/java/java/util/concurrent/locks/ReadWriteLock.java23
-rw-r--r--concurrent/src/main/java/java/util/concurrent/locks/ReentrantLock.java413
-rw-r--r--concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java1102
-rw-r--r--concurrent/src/main/java/java/util/concurrent/locks/package-info.java47
-rw-r--r--concurrent/src/main/java/java/util/concurrent/locks/package.html44
-rw-r--r--concurrent/src/main/java/java/util/concurrent/package-info.java233
-rw-r--r--concurrent/src/main/java/java/util/concurrent/package.html125
-rw-r--r--concurrent/src/test/java/tests/api/java/util/concurrent/AbstractQueuedSynchronizerTest.java3
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/ArrayBlockingQueueTest.java19
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerFieldUpdaterTest.java22
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerTest.java1
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongFieldUpdaterTest.java41
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongTest.java1
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceFieldUpdaterTest.java36
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceTest.java4
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/ConcurrentHashMapTest.java132
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/CyclicBarrierTest.java253
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/DelayQueueTest.java224
-rw-r--r--concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorCompletionServiceTest.java8
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/ExecutorsTest.java94
-rw-r--r--concurrent/src/test/java/tests/api/java/util/concurrent/JSR166TestCase.java110
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/LinkedBlockingQueueTest.java126
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/LockSupportTest.java27
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/PriorityBlockingQueueTest.java24
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/ReentrantLockTest.java244
-rw-r--r--concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantReadWriteLockTest.java549
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/SynchronousQueueTest.java1
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/ThreadPoolExecutorTest.java66
-rwxr-xr-xconcurrent/src/test/java/tests/api/java/util/concurrent/TimeUnitTest.java76
-rw-r--r--dalvik/src/main/java/dalvik/system/VMStack.java7
82 files changed, 12807 insertions, 7485 deletions
diff --git a/concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java b/concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java
index ed4ce4f..f7ed15c 100644
--- a/concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java
+++ b/concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java
@@ -5,11 +5,10 @@
*/
package java.util.concurrent;
-
import java.util.*;
/**
- * Provides default implementation of {@link ExecutorService}
+ * Provides default implementations of {@link ExecutorService}
* execution methods. This class implements the <tt>submit</tt>,
* <tt>invokeAny</tt> and <tt>invokeAll</tt> methods using the default
* {@link FutureTask} class provided in this package. For example,
@@ -24,6 +23,10 @@ import java.util.*;
*/
public abstract class AbstractExecutorService implements ExecutorService {
+ /**
+ * @throws RejectedExecutionException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ */
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
FutureTask<Object> ftask = new FutureTask<Object>(task, null);
@@ -31,6 +34,10 @@ public abstract class AbstractExecutorService implements ExecutorService {
return ftask;
}
+ /**
+ * @throws RejectedExecutionException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ */
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
FutureTask<T> ftask = new FutureTask<T>(task, result);
@@ -38,6 +45,10 @@ public abstract class AbstractExecutorService implements ExecutorService {
return ftask;
}
+ /**
+ * @throws RejectedExecutionException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ */
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
FutureTask<T> ftask = new FutureTask<T>(task);
@@ -57,7 +68,7 @@ public abstract class AbstractExecutorService implements ExecutorService {
if (ntasks == 0)
throw new IllegalArgumentException();
List<Future<T>> futures= new ArrayList<Future<T>>(ntasks);
- ExecutorCompletionService<T> ecs =
+ ExecutorCompletionService<T> ecs =
new ExecutorCompletionService<T>(this);
// For efficiency, especially in executors with limited
@@ -79,14 +90,14 @@ public abstract class AbstractExecutorService implements ExecutorService {
int active = 1;
for (;;) {
- Future<T> f = ecs.poll();
+ Future<T> f = ecs.poll();
if (f == null) {
if (ntasks > 0) {
--ntasks;
futures.add(ecs.submit(it.next()));
++active;
}
- else if (active == 0)
+ else if (active == 0)
break;
else if (timed) {
f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
@@ -96,29 +107,29 @@ public abstract class AbstractExecutorService implements ExecutorService {
nanos -= now - lastTime;
lastTime = now;
}
- else
+ else
f = ecs.take();
}
if (f != null) {
--active;
try {
return f.get();
- } catch(InterruptedException ie) {
+ } catch (InterruptedException ie) {
throw ie;
- } catch(ExecutionException eex) {
+ } catch (ExecutionException eex) {
ee = eex;
- } catch(RuntimeException rex) {
+ } catch (RuntimeException rex) {
ee = new ExecutionException(rex);
}
}
- }
+ }
if (ee == null)
ee = new ExecutionException();
throw ee;
} finally {
- for (Future<T> f : futures)
+ for (Future<T> f : futures)
f.cancel(true);
}
}
@@ -133,8 +144,8 @@ public abstract class AbstractExecutorService implements ExecutorService {
}
}
- public <T> T invokeAny(Collection<Callable<T>> tasks,
- long timeout, TimeUnit unit)
+ public <T> T invokeAny(Collection<Callable<T>> tasks,
+ long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return doInvokeAny(tasks, true, unit.toNanos(timeout));
}
@@ -153,10 +164,10 @@ public abstract class AbstractExecutorService implements ExecutorService {
}
for (Future<T> f : futures) {
if (!f.isDone()) {
- try {
- f.get();
- } catch(CancellationException ignore) {
- } catch(ExecutionException ignore) {
+ try {
+ f.get();
+ } catch (CancellationException ignore) {
+ } catch (ExecutionException ignore) {
}
}
}
@@ -164,13 +175,13 @@ public abstract class AbstractExecutorService implements ExecutorService {
return futures;
} finally {
if (!done)
- for (Future<T> f : futures)
+ for (Future<T> f : futures)
f.cancel(true);
}
}
- public <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks,
- long timeout, TimeUnit unit)
+ public <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks,
+ long timeout, TimeUnit unit)
throws InterruptedException {
if (tasks == null || unit == null)
throw new NullPointerException();
@@ -178,7 +189,7 @@ public abstract class AbstractExecutorService implements ExecutorService {
List<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
boolean done = false;
try {
- for (Callable<T> t : tasks)
+ for (Callable<T> t : tasks)
futures.add(new FutureTask<T>(t));
long lastTime = System.nanoTime();
@@ -192,18 +203,18 @@ public abstract class AbstractExecutorService implements ExecutorService {
nanos -= now - lastTime;
lastTime = now;
if (nanos <= 0)
- return futures;
+ return futures;
}
for (Future<T> f : futures) {
if (!f.isDone()) {
- if (nanos <= 0)
- return futures;
- try {
- f.get(nanos, TimeUnit.NANOSECONDS);
- } catch(CancellationException ignore) {
- } catch(ExecutionException ignore) {
- } catch(TimeoutException toe) {
+ if (nanos <= 0)
+ return futures;
+ try {
+ f.get(nanos, TimeUnit.NANOSECONDS);
+ } catch (CancellationException ignore) {
+ } catch (ExecutionException ignore) {
+ } catch (TimeoutException toe) {
return futures;
}
long now = System.nanoTime();
@@ -215,7 +226,7 @@ public abstract class AbstractExecutorService implements ExecutorService {
return futures;
} finally {
if (!done)
- for (Future<T> f : futures)
+ for (Future<T> f : futures)
f.cancel(true);
}
}
diff --git a/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java b/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java
index 43b9fd0..0082f07 100644
--- a/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java
+++ b/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java
@@ -24,8 +24,8 @@ import java.util.*;
* <p>This is a classic &quot;bounded buffer&quot;, in which a
* fixed-sized array holds elements inserted by producers and
* extracted by consumers. Once created, the capacity cannot be
- * increased. Attempts to offer an element to a full queue will
- * result in the offer operation blocking; attempts to retrieve an
+ * 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
@@ -35,8 +35,9 @@ import java.util.*;
* generally decreases throughput but reduces variability and avoids
* starvation.
*
- * <p>This class implements all of the <em>optional</em> methods
- * of the {@link Collection} and {@link Iterator} interfaces.
+ * <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
@@ -56,9 +57,9 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
/** The queued items */
private final E[] items;
/** items index for next take, poll or remove */
- private transient int takeIndex;
+ private int takeIndex;
/** items index for next put, offer, or add. */
- private transient int putIndex;
+ private int putIndex;
/** Number of items in the queue */
private int count;
@@ -84,7 +85,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
}
/**
- * Insert element at current put position, advance, and signal.
+ * Inserts element at current put position, advances, and signals.
* Call only when holding lock.
*/
private void insert(E x) {
@@ -95,7 +96,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
}
/**
- * Extract element at current take position, advance, and signal.
+ * Extracts element at current take position, advances, and signals.
* Call only when holding lock.
*/
private E extract() {
@@ -139,6 +140,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
/**
* 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
*/
@@ -149,10 +151,11 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
/**
* 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.
+ * 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) {
@@ -169,15 +172,16 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
* 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.
+ * 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 <tt>c</tt> or any element within it
- * is <tt>null</tt>
+ * <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) {
@@ -190,23 +194,38 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
}
/**
- * Inserts the specified element at the tail of this queue if possible,
- * returning immediately if this queue is full.
+ * 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.
*
- * @param o the element to add.
- * @return <tt>true</tt> if it was possible to add the element to
- * this queue, else <tt>false</tt>
- * @throws NullPointerException if the specified element is <tt>null</tt>
+ * @throws NullPointerException if the specified element is null
*/
- public boolean offer(E o) {
- if (o == null) throw new NullPointerException();
+ 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(o);
+ insert(e);
return true;
}
} finally {
@@ -215,29 +234,50 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
}
/**
- * Inserts the specified element at the tail of this queue, waiting if
- * necessary up to the specified wait time for space to become available.
- * @param o the element to add
- * @param timeout how long to wait before giving up, in units of
- * <tt>unit</tt>
- * @param unit a <tt>TimeUnit</tt> determining how to interpret the
- * <tt>timeout</tt> parameter
- * @return <tt>true</tt> if successful, or <tt>false</tt> if
- * the specified waiting time elapses before space is available.
- * @throws InterruptedException if interrupted while waiting.
- * @throws NullPointerException if the specified element is <tt>null</tt>.
+ * 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 o, long timeout, TimeUnit unit)
+ public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
- if (o == null) throw new NullPointerException();
+ if (e == null) throw new NullPointerException();
+ long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
- long nanos = unit.toNanos(timeout);
for (;;) {
if (count != items.length) {
- insert(o);
+ insert(e);
return true;
}
if (nanos <= 0)
@@ -254,7 +294,6 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
}
}
-
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
@@ -268,11 +307,29 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
}
}
+ 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 {
- long nanos = unit.toNanos(timeout);
for (;;) {
if (count != 0) {
E x = extract();
@@ -293,30 +350,6 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
}
}
-
- 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();
- }
- }
-
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
@@ -327,56 +360,12 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
}
}
- 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();
- }
- }
-
- /**
- * Adds the specified element to the tail of this queue, waiting if
- * necessary for space to become available.
- * @param o the element to add
- * @throws InterruptedException if interrupted while waiting.
- * @throws NullPointerException if the specified element is <tt>null</tt>.
- */
- public void put(E o) throws InterruptedException {
- if (o == 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(o);
- } 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.
+ * @return the number of elements in this queue
*/
public int size() {
final ReentrantLock lock = this.lock;
@@ -391,17 +380,15 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
// this doc comment is a modified copy of the inherited doc comment,
// without the reference to unlimited queues.
/**
- * Returns the number of elements that this queue can ideally (in
- * the absence of memory or resource constraints) accept without
+ * 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 <tt>add</tt> an element will succeed by
- * inspecting <tt>remainingCapacity</tt> because it may be the
- * case that a waiting consumer is ready to <tt>take</tt> an
- * element out of an otherwise full queue.
- *
- * @return the remaining capacity
+ *
+ * <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;
@@ -413,7 +400,48 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
}
}
+ /**
+ * 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;
@@ -433,6 +461,19 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
}
}
+ /**
+ * 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;
@@ -451,6 +492,42 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
}
}
+ /**
+ * 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;
@@ -486,7 +563,10 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
}
}
-
+ /**
+ * 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;
@@ -507,6 +587,12 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
}
}
+ /**
+ * @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();
@@ -537,7 +623,12 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
}
}
-
+ /**
+ * @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();
@@ -574,12 +665,12 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
/**
* 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 java.util.ConcurrentModificationException},
+ * 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.
+ * @return an iterator over the elements in this queue in proper sequence
*/
public Iterator<E> iterator() {
final ReentrantLock lock = this.lock;
@@ -606,7 +697,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
* 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;
/**
@@ -635,7 +726,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E>
}
/**
- * Check whether nextIndex is valid; if so setting nextItem.
+ * Checks whether nextIndex is valid; if so setting nextItem.
* Stops iterator when either hits putIndex or sees null item.
*/
private void checkNext() {
diff --git a/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java b/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java
index ddb7d77..85945b9 100644
--- a/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java
+++ b/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java
@@ -15,9 +15,50 @@ import java.util.Queue;
/**
* A {@link java.util.Queue} that additionally supports operations
- * that wait for the queue to become non-empty when retrieving an element,
- * and wait for space to become available in the queue when storing an
- * element.
+ * that wait for the queue to become non-empty when retrieving an
+ * element, and wait for space to become available in the queue when
+ * storing an element.
+ *
+ * <p><tt>BlockingQueue</tt> methods come in four forms, with different ways
+ * of handling operations that cannot be satisfied immediately, but may be
+ * satisfied at some point in the future:
+ * one throws an exception, the second returns a special value (either
+ * <tt>null</tt> or <tt>false</tt>, depending on the operation), the third
+ * blocks the current thread indefinitely until the operation can succeed,
+ * and the fourth blocks for only a given maximum time limit before giving
+ * up. These methods are summarized in the following table:
+ *
+ * <p>
+ * <table BORDER CELLPADDING=3 CELLSPACING=1>
+ * <tr>
+ * <td></td>
+ * <td ALIGN=CENTER><em>Throws exception</em></td>
+ * <td ALIGN=CENTER><em>Special value</em></td>
+ * <td ALIGN=CENTER><em>Blocks</em></td>
+ * <td ALIGN=CENTER><em>Times out</em></td>
+ * </tr>
+ * <tr>
+ * <td><b>Insert</b></td>
+ * <td>{@link #add add(e)}</td>
+ * <td>{@link #offer offer(e)}</td>
+ * <td>{@link #put put(e)}</td>
+ * <td>{@link #offer(Object, long, TimeUnit) offer(e, time, unit)}</td>
+ * </tr>
+ * <tr>
+ * <td><b>Remove</b></td>
+ * <td>{@link #remove remove()}</td>
+ * <td>{@link #poll poll()}</td>
+ * <td>{@link #take take()}</td>
+ * <td>{@link #poll(long, TimeUnit) poll(time, unit)}</td>
+ * </tr>
+ * <tr>
+ * <td><b>Examine</b></td>
+ * <td>{@link #element element()}</td>
+ * <td>{@link #peek peek()}</td>
+ * <td><em>not applicable</em></td>
+ * <td><em>not applicable</em></td>
+ * </tr>
+ * </table>
*
* <p>A <tt>BlockingQueue</tt> does not accept <tt>null</tt> elements.
* Implementations throw <tt>NullPointerException</tt> on attempts
@@ -31,16 +72,22 @@ import java.util.Queue;
* A <tt>BlockingQueue</tt> without any intrinsic capacity constraints always
* reports a remaining capacity of <tt>Integer.MAX_VALUE</tt>.
*
- * <p> While <tt>BlockingQueue</tt> is designed to be used primarily
- * for producer-consumer queues, it additionally supports the {@link
- * java.util.Collection} interface. So, for example, it is possible
- * to remove an arbitrary element from a queue using
+ * <p> <tt>BlockingQueue</tt> implementations are designed to be used
+ * primarily for producer-consumer queues, but additionally support
+ * the {@link java.util.Collection} interface. So, for example, it is
+ * possible to remove an arbitrary element from a queue using
* <tt>remove(x)</tt>. However, such operations are in general
* <em>not</em> performed very efficiently, and are intended for only
- * occasional use, such as when a queued message is cancelled. Also,
- * the bulk Collection operations, most notably <tt>addAll</tt>, are
- * <em>not</em> necessarily performed atomically, so it is possible
- * for <tt>addAll(c)</tt> to fail (throwing an exception) after adding
+ * occasional use, such as when a queued message is cancelled.
+ *
+ * <p> <tt>BlockingQueue</tt> implementations are thread-safe. All
+ * queuing methods achieve their effects atomically using internal
+ * locks or other forms of concurrency control. However, the
+ * <em>bulk</em> Collection operations <tt>addAll</tt>,
+ * <tt>containsAll</tt>, <tt>retainAll</tt> and <tt>removeAll</tt> are
+ * <em>not</em> necessarily performed atomically unless specified
+ * otherwise in an implementation. So it is possible, for example, for
+ * <tt>addAll(c)</tt> to fail (throwing an exception) after adding
* only some of the elements in <tt>c</tt>.
*
* <p>A <tt>BlockingQueue</tt> does <em>not</em> intrinsically support
@@ -61,7 +108,7 @@ import java.util.Queue;
* Producer(BlockingQueue q) { queue = q; }
* public void run() {
* try {
- * while(true) { queue.put(produce()); }
+ * while (true) { queue.put(produce()); }
* } catch (InterruptedException ex) { ... handle ...}
* }
* Object produce() { ... }
@@ -72,7 +119,7 @@ import java.util.Queue;
* Consumer(BlockingQueue q) { queue = q; }
* public void run() {
* try {
- * while(true) { consume(queue.take()); }
+ * while (true) { consume(queue.take()); }
* } catch (InterruptedException ex) { ... handle ...}
* }
* void consume(Object x) { ... }
@@ -91,137 +138,207 @@ import java.util.Queue;
* }
* </pre>
*
+ * <p>Memory consistency effects: As with other concurrent
+ * collections, actions in a thread prior to placing an object into a
+ * {@code BlockingQueue}
+ * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
+ * actions subsequent to the access or removal of that element from
+ * the {@code BlockingQueue} in another thread.
+ *
* @since 1.5
* @author Doug Lea
* @param <E> the type of elements held in this collection
*/
public interface BlockingQueue<E> extends Queue<E> {
+ /**
+ * Inserts the specified element into this queue if it is possible to do
+ * so immediately without violating capacity restrictions, returning
+ * <tt>true</tt> upon success and throwing an
+ * <tt>IllegalStateException</tt> if no space is currently available.
+ * When using a capacity-restricted queue, it is generally preferable to
+ * use {@link #offer(Object) offer}.
+ *
+ * @param e the element to add
+ * @return <tt>true</tt> (as specified by {@link Collection#add})
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this queue
+ * @throws NullPointerException if the specified element is null
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this queue
+ */
+ boolean add(E e);
/**
- * Inserts the specified element into this queue, if possible. When
- * using queues that may impose insertion restrictions (for
- * example capacity bounds), method <tt>offer</tt> is generally
- * preferable to method {@link Collection#add}, which can fail to
- * insert an element only by throwing an exception.
+ * Inserts the specified element into this queue if it is possible to do
+ * so immediately without violating capacity restrictions, returning
+ * <tt>true</tt> upon success and <tt>false</tt> if no space is currently
+ * available. When using a capacity-restricted queue, this method is
+ * generally preferable to {@link #add}, which can fail to insert an
+ * element only by throwing an exception.
*
- * @param o the element to add.
- * @return <tt>true</tt> if it was possible to add the element to
- * this queue, else <tt>false</tt>
- * @throws NullPointerException if the specified element is <tt>null</tt>
+ * @param e the element to add
+ * @return <tt>true</tt> if the element was added to this queue, else
+ * <tt>false</tt>
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this queue
+ * @throws NullPointerException if the specified element is null
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this queue
*/
- boolean offer(E o);
-
+ boolean offer(E e);
+
/**
* Inserts the specified element into this queue, waiting if necessary
- * up to the specified wait time for space to become available.
- * @param o the element to add
- * @param timeout how long to wait before giving up, in units of
- * <tt>unit</tt>
- * @param unit a <tt>TimeUnit</tt> determining how to interpret the
- * <tt>timeout</tt> parameter
- * @return <tt>true</tt> if successful, or <tt>false</tt> if
- * the specified waiting time elapses before space is available.
- * @throws InterruptedException if interrupted while waiting.
- * @throws NullPointerException if the specified element is <tt>null</tt>.
+ * for space to become available.
+ *
+ * @param e the element to add
+ * @throws InterruptedException if interrupted while waiting
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this queue
+ * @throws NullPointerException if the specified element is null
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this queue
*/
- boolean offer(E o, long timeout, TimeUnit unit)
- throws InterruptedException;
+ void put(E e) throws InterruptedException;
/**
- * Retrieves and removes the head of this queue, waiting
- * if necessary up to the specified wait time if no elements are
- * present on this queue.
+ * Inserts the specified element into this queue, waiting up to the
+ * specified wait time if necessary for space to become available.
+ *
+ * @param e the element to add
* @param timeout how long to wait before giving up, in units of
- * <tt>unit</tt>
+ * <tt>unit</tt>
* @param unit a <tt>TimeUnit</tt> determining how to interpret the
- * <tt>timeout</tt> parameter
- * @return the head of this queue, or <tt>null</tt> if the
- * specified waiting time elapses before an element is present.
- * @throws InterruptedException if interrupted while waiting.
+ * <tt>timeout</tt> parameter
+ * @return <tt>true</tt> if successful, or <tt>false</tt> if
+ * the specified waiting time elapses before space is available
+ * @throws InterruptedException if interrupted while waiting
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this queue
+ * @throws NullPointerException if the specified element is null
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this queue
*/
- E poll(long timeout, TimeUnit unit)
+ boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException;
/**
- * Retrieves and removes the head of this queue, waiting
- * if no elements are present on this queue.
+ * Retrieves and removes the head of this queue, waiting if necessary
+ * until an element becomes available.
+ *
* @return the head of this queue
- * @throws InterruptedException if interrupted while waiting.
+ * @throws InterruptedException if interrupted while waiting
*/
E take() throws InterruptedException;
/**
- * Adds the specified element to this queue, waiting if necessary for
- * space to become available.
- * @param o the element to add
- * @throws InterruptedException if interrupted while waiting.
- * @throws NullPointerException if the specified element is <tt>null</tt>.
+ * Retrieves and removes the head of this queue, waiting up to the
+ * specified wait time if necessary for an element to become available.
+ *
+ * @param timeout how long to wait before giving up, in units of
+ * <tt>unit</tt>
+ * @param unit a <tt>TimeUnit</tt> determining how to interpret the
+ * <tt>timeout</tt> parameter
+ * @return the head of this queue, or <tt>null</tt> if the
+ * specified waiting time elapses before an element is available
+ * @throws InterruptedException if interrupted while waiting
*/
- void put(E o) throws InterruptedException;
+ E poll(long timeout, TimeUnit unit)
+ throws InterruptedException;
/**
- * Returns the number of elements that this queue can ideally (in
- * the absence of memory or resource constraints) accept without
- * blocking, or <tt>Integer.MAX_VALUE</tt> if there is no
- * intrinsic limit.
- * <p>Note that you <em>cannot</em> always tell if
- * an attempt to <tt>add</tt> an element will succeed by
- * inspecting <tt>remainingCapacity</tt> because it may be the
- * case that a waiting consumer is ready to <tt>take</tt> an
- * element out of an otherwise full queue.
+ * Returns the number of additional elements that this queue can ideally
+ * (in the absence of memory or resource constraints) accept without
+ * blocking, or <tt>Integer.MAX_VALUE</tt> if there is no intrinsic
+ * limit.
+ *
+ * <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.
+ *
* @return the remaining capacity
*/
int remainingCapacity();
/**
- * Adds the specified element to this queue if it is possible to
- * do so immediately, returning <tt>true</tt> upon success, else
- * throwing an IllegalStateException.
- * @param o the element
- * @return <tt>true</tt> (as per the general contract of
- * <tt>Collection.add</tt>).
+ * 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
+ * @throws ClassCastException if the class of the specified element
+ * is incompatible with this queue (optional)
+ * @throws NullPointerException if the specified element is null (optional)
+ */
+ boolean remove(Object o);
+
+ /**
+ * 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>.
*
- * @throws NullPointerException if the specified element is <tt>null</tt>
- * @throws IllegalStateException if element cannot be added
+ * @param o object to be checked for containment in this queue
+ * @return <tt>true</tt> if this queue contains the specified element
+ * @throws ClassCastException if the class of the specified element
+ * is incompatible with this queue (optional)
+ * @throws NullPointerException if the specified element is null (optional)
*/
- boolean add(E o);
+ public boolean contains(Object o);
/**
* Removes all available elements from this queue and adds them
- * into the given collection. This operation may be more
+ * to the given collection. This operation may be more
* efficient than repeatedly polling this queue. A failure
- * encountered while attempting to <tt>add</tt> elements to
+ * encountered while attempting to add elements to
* collection <tt>c</tt> may result in elements being in neither,
* either or both collections when the associated exception is
- * thrown. Attempts to drain a queue to itself result in
+ * thrown. Attempts to drain a queue to itself result in
* <tt>IllegalArgumentException</tt>. Further, the behavior of
* this operation is undefined if the specified collection is
* modified while the operation is in progress.
*
* @param c the collection to transfer elements into
- * @return the number of elements transferred.
- * @throws NullPointerException if c is null
- * @throws IllegalArgumentException if c is this queue
- *
+ * @return the number of elements transferred
+ * @throws UnsupportedOperationException if addition of elements
+ * is not supported by the specified collection
+ * @throws ClassCastException if the class of an element of this queue
+ * prevents it from being added to the specified collection
+ * @throws NullPointerException if the specified collection is null
+ * @throws IllegalArgumentException if the specified collection is this
+ * queue, or some property of an element of this queue prevents
+ * it from being added to the specified collection
*/
int drainTo(Collection<? super E> c);
-
+
/**
* Removes at most the given number of available elements from
- * this queue and adds them into the given collection. A failure
- * encountered while attempting to <tt>add</tt> elements to
+ * this queue and adds them to the given collection. A failure
+ * encountered while attempting to add elements to
* collection <tt>c</tt> may result in elements being in neither,
* either or both collections when the associated exception is
- * thrown. Attempts to drain a queue to itself result in
+ * thrown. Attempts to drain a queue to itself result in
* <tt>IllegalArgumentException</tt>. Further, the behavior of
* this operation is undefined if the specified collection is
* modified while the operation is in progress.
*
* @param c the collection to transfer elements into
* @param maxElements the maximum number of elements to transfer
- * @return the number of elements transferred.
- * @throws NullPointerException if c is null
- * @throws IllegalArgumentException if c is this queue
+ * @return the number of elements transferred
+ * @throws UnsupportedOperationException if addition of elements
+ * is not supported by the specified collection
+ * @throws ClassCastException if the class of an element of this queue
+ * prevents it from being added to the specified collection
+ * @throws NullPointerException if the specified collection is null
+ * @throws IllegalArgumentException if the specified collection is this
+ * queue, or some property of an element of this queue prevents
+ * it from being added to the specified collection
*/
int drainTo(Collection<? super E> c, int maxElements);
}
diff --git a/concurrent/src/main/java/java/util/concurrent/CompletionService.java b/concurrent/src/main/java/java/util/concurrent/CompletionService.java
index e349e5f..df9f719 100644
--- a/concurrent/src/main/java/java/util/concurrent/CompletionService.java
+++ b/concurrent/src/main/java/java/util/concurrent/CompletionService.java
@@ -16,52 +16,56 @@ package java.util.concurrent;
* submitted in one part of a program or system, and then acted upon
* in a different part of the program when the reads complete,
* possibly in a different order than they were requested.
-
- * <p>
*
- * Typically, a <tt>CompletionService</tt> relies on a separate {@link
- * Executor} to actually execute the tasks, in which case the
+ * <p>Typically, a <tt>CompletionService</tt> relies on a separate
+ * {@link Executor} to actually execute the tasks, in which case the
* <tt>CompletionService</tt> only manages an internal completion
* queue. The {@link ExecutorCompletionService} class provides an
* implementation of this approach.
*
+ * <p>Memory consistency effects: Actions in a thread prior to
+ * submitting a task to a {@code CompletionService}
+ * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
+ * actions taken by that task, which in turn <i>happen-before</i>
+ * actions following a successful return from the corresponding {@code take()}.
+ *
*/
public interface CompletionService<V> {
/**
* Submits a value-returning task for execution and returns a Future
- * representing the pending results of the task. Upon completion,
+ * representing the pending results of the task. Upon completion,
* this task may be taken or polled.
*
* @param task the task to submit
* @return a Future representing pending completion of the task
- * @throws RejectedExecutionException if task cannot be scheduled
- * for execution
- * @throws NullPointerException if task null
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ * @throws NullPointerException if the task is null
*/
Future<V> submit(Callable<V> task);
-
/**
- * Submits a Runnable task for execution and returns a Future
- * representing that task. Upon completion,
- * this task may be taken or polled.
+ * Submits a Runnable task for execution and returns a Future
+ * representing that task. Upon completion, this task may be
+ * taken or polled.
*
* @param task the task to submit
* @param result the result to return upon successful completion
* @return a Future representing pending completion of the task,
- * and whose <tt>get()</tt> method will return the given result value
- * upon completion
- * @throws RejectedExecutionException if task cannot be scheduled
- * for execution
- * @throws NullPointerException if task null
+ * and whose <tt>get()</tt> method will return the given
+ * result value upon completion
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ * @throws NullPointerException if the task is null
*/
Future<V> submit(Runnable task, V result);
/**
* Retrieves and removes the Future representing the next
* completed task, waiting if none are yet present.
+ *
* @return the Future representing the next completed task
- * @throws InterruptedException if interrupted while waiting.
+ * @throws InterruptedException if interrupted while waiting
*/
Future<V> take() throws InterruptedException;
@@ -71,7 +75,7 @@ public interface CompletionService<V> {
* completed task or <tt>null</tt> if none are present.
*
* @return the Future representing the next completed task, or
- * <tt>null</tt> if none are present.
+ * <tt>null</tt> if none are present
*/
Future<V> poll();
@@ -79,14 +83,15 @@ public interface CompletionService<V> {
* Retrieves and removes the Future representing the next
* completed task, waiting if necessary up to the specified wait
* time if none are yet present.
+ *
* @param timeout how long to wait before giving up, in units of
- * <tt>unit</tt>
+ * <tt>unit</tt>
* @param unit a <tt>TimeUnit</tt> determining how to interpret the
- * <tt>timeout</tt> parameter
+ * <tt>timeout</tt> parameter
* @return the Future representing the next completed task or
- * <tt>null</tt> if the specified waiting time elapses before one
- * is present.
- * @throws InterruptedException if interrupted while waiting.
+ * <tt>null</tt> if the specified waiting time elapses
+ * before one is present
+ * @throws InterruptedException if interrupted while waiting
*/
Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;
}
diff --git a/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java b/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java
index f72c6fe..cb5fb3f 100644
--- a/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java
+++ b/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java
@@ -14,7 +14,6 @@ import java.io.ObjectOutputStream;
// BEGIN android-note
// removed link to collections framework docs
-// removed cloneable interface from ConcurrentMap interface
// END android-note
/**
@@ -38,13 +37,12 @@ import java.io.ObjectOutputStream;
* removal of only some entries. Similarly, Iterators and
* Enumerations return elements reflecting the state of the hash table
* at some point at or since the creation of the iterator/enumeration.
- * They do <em>not</em> throw
- * {@link ConcurrentModificationException}. However, iterators are
- * designed to be used by only one thread at a time.
+ * They do <em>not</em> throw {@link ConcurrentModificationException}.
+ * However, iterators are designed to be used by only one thread at a time.
*
* <p> The allowed concurrency among update operations is guided by
* the optional <tt>concurrencyLevel</tt> constructor argument
- * (default 16), which is used as a hint for internal sizing. The
+ * (default <tt>16</tt>), which is used as a hint for internal sizing. The
* table is internally partitioned to try to permit the indicated
* number of concurrent updates without contention. Because placement
* in hash tables is essentially random, the actual concurrency will
@@ -54,20 +52,23 @@ import java.io.ObjectOutputStream;
* and a significantly lower value can lead to thread contention. But
* overestimates and underestimates within an order of magnitude do
* not usually have much noticeable impact. A value of one is
- * appropriate when it is known that only one thread will modify
- * and all others will only read.
+ * appropriate when it is known that only one thread will modify and
+ * all others will only read. Also, resizing this or any other kind of
+ * hash table is a relatively slow operation, so, when possible, it is
+ * a good idea to provide estimates of expected table sizes in
+ * constructors.
*
- * <p>This class implements all of the <em>optional</em> methods
- * of the {@link Map} and {@link Iterator} interfaces.
+ * <p>This class and its views and iterators implement all of the
+ * <em>optional</em> methods of the {@link Map} and {@link Iterator}
+ * interfaces.
*
- * <p> Like {@link java.util.Hashtable} but unlike {@link
- * java.util.HashMap}, this class does NOT allow <tt>null</tt> to be
- * used as a key or value.
+ * <p> Like {@link Hashtable} but unlike {@link HashMap}, this class
+ * does <em>not</em> allow <tt>null</tt> to be used as a key or value.
*
* @since 1.5
* @author Doug Lea
* @param <K> the type of keys maintained by this map
- * @param <V> the type of mapped values
+ * @param <V> the type of mapped values
*/
public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
implements ConcurrentMap<K, V>, Serializable {
@@ -81,29 +82,30 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
/* ---------------- Constants -------------- */
/**
- * The default initial number of table slots for this table.
- * Used when not otherwise specified in constructor.
+ * The default initial capacity for this table,
+ * used when not otherwise specified in a constructor.
*/
- static int DEFAULT_INITIAL_CAPACITY = 16;
+ static final int DEFAULT_INITIAL_CAPACITY = 16;
/**
- * The maximum capacity, used if a higher value is implicitly
- * specified by either of the constructors with arguments. MUST
- * be a power of two <= 1<<30 to ensure that entries are indexible
- * using ints.
+ * The default load factor for this table, used when not
+ * otherwise specified in a constructor.
*/
- static final int MAXIMUM_CAPACITY = 1 << 30;
+ static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
- * The default load factor for this table. Used when not
- * otherwise specified in constructor.
+ * The default concurrency level for this table, used when not
+ * otherwise specified in a constructor.
*/
- static final float DEFAULT_LOAD_FACTOR = 0.75f;
+ static final int DEFAULT_CONCURRENCY_LEVEL = 16;
/**
- * The default number of concurrency control segments.
- **/
- static final int DEFAULT_SEGMENTS = 16;
+ * The maximum capacity, used if a higher value is implicitly
+ * specified by either of the constructors with arguments. MUST
+ * be a power of two <= 1<<30 to ensure that entries are indexable
+ * using ints.
+ */
+ static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* The maximum number of segments to allow; used to bound
@@ -111,23 +113,31 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
*/
static final int MAX_SEGMENTS = 1 << 16; // slightly conservative
+ /**
+ * Number of unsynchronized retries in size and containsValue
+ * methods before resorting to locking. This is used to avoid
+ * unbounded retries if tables undergo continuous modification
+ * which would make it impossible to obtain an accurate result.
+ */
+ static final int RETRIES_BEFORE_LOCK = 2;
+
/* ---------------- Fields -------------- */
/**
* Mask value for indexing into segments. The upper bits of a
* key's hash code are used to choose the segment.
- **/
+ */
final int segmentMask;
/**
* Shift value for indexing within segments.
- **/
+ */
final int segmentShift;
/**
* The segments, each of which is a specialized hash table
*/
- final Segment[] segments;
+ final Segment<K,V>[] segments;
transient Set<K> keySet;
transient Set<Map.Entry<K,V>> entrySet;
@@ -136,18 +146,21 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
/* ---------------- Small Utilities -------------- */
/**
- * Returns a hash code for non-null Object x.
- * Uses the same hash code spreader as most other java.util hash tables.
- * @param x the object serving as a key
- * @return the hash code
+ * Applies a supplemental hash function to a given hashCode, which
+ * defends against poor quality hash functions. This is critical
+ * because ConcurrentHashMap uses power-of-two length hash tables,
+ * that otherwise encounter collisions for hashCodes that do not
+ * differ in lower or upper bits.
*/
- static int hash(Object x) {
- int h = x.hashCode();
- h += ~(h << 9);
- h ^= (h >>> 14);
- h += (h << 4);
- h ^= (h >>> 10);
- return h;
+ private static int hash(int h) {
+ // Spread bits to regularize both segment and index locations,
+ // using variant of single-word Wang/Jenkins hash.
+ h += (h << 15) ^ 0xffffcd7d;
+ h ^= (h >>> 10);
+ h += (h << 3);
+ h ^= (h >>> 6);
+ h += (h << 2) + (h << 14);
+ return h ^ (h >>> 16);
}
/**
@@ -156,16 +169,47 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
* @return the segment
*/
final Segment<K,V> segmentFor(int hash) {
- return (Segment<K,V>) segments[(hash >>> segmentShift) & segmentMask];
+ return segments[(hash >>> segmentShift) & segmentMask];
}
/* ---------------- Inner Classes -------------- */
/**
+ * ConcurrentHashMap list entry. Note that this is never exported
+ * out as a user-visible Map.Entry.
+ *
+ * Because the value field is volatile, not final, it is legal wrt
+ * the Java Memory Model for an unsynchronized reader to see null
+ * instead of initial value when read via a data race. Although a
+ * reordering leading to this is not likely to ever actually
+ * occur, the Segment.readValueUnderLock method is used as a
+ * backup in case a null (pre-initialized) value is ever seen in
+ * an unsynchronized access method.
+ */
+ static final class HashEntry<K,V> {
+ final K key;
+ final int hash;
+ volatile V value;
+ final HashEntry<K,V> next;
+
+ HashEntry(K key, int hash, HashEntry<K,V> next, V value) {
+ this.key = key;
+ this.hash = hash;
+ this.next = next;
+ this.value = value;
+ }
+
+ @SuppressWarnings("unchecked")
+ static final <K,V> HashEntry<K,V>[] newArray(int i) {
+ return new HashEntry[i];
+ }
+ }
+
+ /**
* Segments are specialized versions of hash tables. This
* subclasses from ReentrantLock opportunistically, just to
* simplify some locking and avoid separate construction.
- **/
+ */
static final class Segment<K,V> extends ReentrantLock implements Serializable {
/*
* Segments maintain a table of entry lists that are ALWAYS
@@ -179,58 +223,59 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
* is less than two for the default load factor threshold.)
*
* Read operations can thus proceed without locking, but rely
- * on a memory barrier to ensure that completed write
- * operations performed by other threads are
- * noticed. Conveniently, the "count" field, tracking the
- * number of elements, can also serve as the volatile variable
- * providing proper read/write barriers. This is convenient
- * because this field needs to be read in many read operations
- * anyway.
- *
- * Implementors note. The basic rules for all this are:
+ * on selected uses of volatiles to ensure that completed
+ * write operations performed by other threads are
+ * noticed. For most purposes, the "count" field, tracking the
+ * number of elements, serves as that volatile variable
+ * ensuring visibility. This is convenient because this field
+ * needs to be read in many read operations anyway:
*
- * - All unsynchronized read operations must first read the
+ * - All (unsynchronized) read operations must first read the
* "count" field, and should not look at table entries if
* it is 0.
*
- * - All synchronized write operations should write to
- * the "count" field after updating. The operations must not
- * take any action that could even momentarily cause
- * a concurrent read operation to see inconsistent
- * data. This is made easier by the nature of the read
- * operations in Map. For example, no operation
+ * - All (synchronized) write operations should write to
+ * the "count" field after structurally changing any bin.
+ * The operations must not take any action that could even
+ * momentarily cause a concurrent read operation to see
+ * inconsistent data. This is made easier by the nature of
+ * the read operations in Map. For example, no operation
* can reveal that the table has grown but the threshold
* has not yet been updated, so there are no atomicity
* requirements for this with respect to reads.
*
- * As a guide, all critical volatile reads and writes are marked
- * in code comments.
+ * As a guide, all critical volatile reads and writes to the
+ * count field are marked in code comments.
*/
private static final long serialVersionUID = 2249069246763182397L;
/**
* The number of elements in this segment's region.
- **/
+ */
transient volatile int count;
/**
- * Number of updates; used for checking lack of modifications
- * in bulk-read methods.
+ * Number of updates that alter the size of the table. This is
+ * used during bulk-read methods to make sure they see a
+ * consistent snapshot: If modCounts change during a traversal
+ * of segments computing size or checking containsValue, then
+ * we might have an inconsistent view of state so (usually)
+ * must retry.
*/
transient int modCount;
/**
* The table is rehashed when its size exceeds this threshold.
- * (The value of this field is always (int)(capacity *
- * loadFactor).)
+ * (The value of this field is always <tt>(int)(capacity *
+ * loadFactor)</tt>.)
*/
transient int threshold;
/**
- * The per-segment table
+ * The per-segment table.
*/
- transient HashEntry[] table;
+ transient volatile HashEntry<K,V>[] table;
/**
* The load factor for the hash table. Even though this value
@@ -242,29 +287,59 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
Segment(int initialCapacity, float lf) {
loadFactor = lf;
- setTable(new HashEntry[initialCapacity]);
+ setTable(HashEntry.<K,V>newArray(initialCapacity));
+ }
+
+ @SuppressWarnings("unchecked")
+ static final <K,V> Segment<K,V>[] newArray(int i) {
+ return new Segment[i];
}
/**
- * Set table to new HashEntry array.
+ * Sets table to new HashEntry array.
* Call only while holding lock or in constructor.
- **/
- void setTable(HashEntry[] newTable) {
- table = newTable;
+ */
+ void setTable(HashEntry<K,V>[] newTable) {
threshold = (int)(newTable.length * loadFactor);
- count = count; // write-volatile
+ table = newTable;
+ }
+
+ /**
+ * Returns properly casted first entry of bin for given hash.
+ */
+ HashEntry<K,V> getFirst(int hash) {
+ HashEntry<K,V>[] tab = table;
+ return tab[hash & (tab.length - 1)];
+ }
+
+ /**
+ * Reads value field of an entry under lock. Called if value
+ * field ever appears to be null. This is possible only if a
+ * compiler happens to reorder a HashEntry initialization with
+ * its table assignment, which is legal under memory model
+ * but is not known to ever occur.
+ */
+ V readValueUnderLock(HashEntry<K,V> e) {
+ lock();
+ try {
+ return e.value;
+ } finally {
+ unlock();
+ }
}
/* Specialized implementations of map methods */
V get(Object key, int hash) {
if (count != 0) { // read-volatile
- HashEntry[] tab = table;
- int index = hash & (tab.length - 1);
- HashEntry<K,V> e = (HashEntry<K,V>) tab[index];
+ HashEntry<K,V> e = getFirst(hash);
while (e != null) {
- if (e.hash == hash && key.equals(e.key))
- return e.value;
+ if (e.hash == hash && key.equals(e.key)) {
+ V v = e.value;
+ if (v != null)
+ return v;
+ return readValueUnderLock(e); // recheck
+ }
e = e.next;
}
}
@@ -273,9 +348,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
boolean containsKey(Object key, int hash) {
if (count != 0) { // read-volatile
- HashEntry[] tab = table;
- int index = hash & (tab.length - 1);
- HashEntry<K,V> e = (HashEntry<K,V>) tab[index];
+ HashEntry<K,V> e = getFirst(hash);
while (e != null) {
if (e.hash == hash && key.equals(e.key))
return true;
@@ -287,12 +360,17 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
boolean containsValue(Object value) {
if (count != 0) { // read-volatile
- HashEntry[] tab = table;
+ HashEntry<K,V>[] tab = table;
int len = tab.length;
- for (int i = 0 ; i < len; i++)
- for (HashEntry<K,V> e = (HashEntry<K,V>)tab[i] ; e != null ; e = e.next)
- if (value.equals(e.value))
+ for (int i = 0 ; i < len; i++) {
+ for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) {
+ V v = e.value;
+ if (v == null) // recheck
+ v = readValueUnderLock(e);
+ if (value.equals(v))
return true;
+ }
+ }
}
return false;
}
@@ -300,27 +378,16 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
boolean replace(K key, int hash, V oldValue, V newValue) {
lock();
try {
- int c = count;
- HashEntry[] tab = table;
- int index = hash & (tab.length - 1);
- HashEntry<K,V> first = (HashEntry<K,V>) tab[index];
- HashEntry<K,V> e = first;
- for (;;) {
- if (e == null)
- return false;
- if (e.hash == hash && key.equals(e.key))
- break;
+ HashEntry<K,V> e = getFirst(hash);
+ while (e != null && (e.hash != hash || !key.equals(e.key)))
e = e.next;
- }
- V v = e.value;
- if (v == null || !oldValue.equals(v))
- return false;
-
- e.value = newValue;
- count = c; // write-volatile
- return true;
-
+ boolean replaced = false;
+ if (e != null && oldValue.equals(e.value)) {
+ replaced = true;
+ e.value = newValue;
+ }
+ return replaced;
} finally {
unlock();
}
@@ -329,24 +396,16 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
V replace(K key, int hash, V newValue) {
lock();
try {
- int c = count;
- HashEntry[] tab = table;
- int index = hash & (tab.length - 1);
- HashEntry<K,V> first = (HashEntry<K,V>) tab[index];
- HashEntry<K,V> e = first;
- for (;;) {
- if (e == null)
- return null;
- if (e.hash == hash && key.equals(e.key))
- break;
+ HashEntry<K,V> e = getFirst(hash);
+ while (e != null && (e.hash != hash || !key.equals(e.key)))
e = e.next;
- }
- V v = e.value;
- e.value = newValue;
- count = c; // write-volatile
- return v;
-
+ V oldValue = null;
+ if (e != null) {
+ oldValue = e.value;
+ e.value = newValue;
+ }
+ return oldValue;
} finally {
unlock();
}
@@ -357,37 +416,38 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
lock();
try {
int c = count;
- HashEntry[] tab = table;
+ if (c++ > threshold) // ensure capacity
+ rehash();
+ HashEntry<K,V>[] tab = table;
int index = hash & (tab.length - 1);
- HashEntry<K,V> first = (HashEntry<K,V>) tab[index];
+ HashEntry<K,V> first = tab[index];
+ HashEntry<K,V> e = first;
+ while (e != null && (e.hash != hash || !key.equals(e.key)))
+ e = e.next;
- for (HashEntry<K,V> e = first; e != null; e = (HashEntry<K,V>) e.next) {
- if (e.hash == hash && key.equals(e.key)) {
- V oldValue = e.value;
- if (!onlyIfAbsent)
- e.value = value;
- ++modCount;
- count = c; // write-volatile
- return oldValue;
- }
+ V oldValue;
+ if (e != null) {
+ oldValue = e.value;
+ if (!onlyIfAbsent)
+ e.value = value;
}
-
- tab[index] = new HashEntry<K,V>(hash, key, value, first);
- ++modCount;
- ++c;
- count = c; // write-volatile
- if (c > threshold)
- setTable(rehash(tab));
- return null;
+ else {
+ oldValue = null;
+ ++modCount;
+ tab[index] = new HashEntry<K,V>(key, hash, first, value);
+ count = c; // write-volatile
+ }
+ return oldValue;
} finally {
unlock();
}
}
- HashEntry[] rehash(HashEntry[] oldTable) {
+ void rehash() {
+ HashEntry<K,V>[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity >= MAXIMUM_CAPACITY)
- return oldTable;
+ return;
/*
* Reclassify nodes in each list to new Map. Because we are
@@ -403,12 +463,13 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
* right now.
*/
- HashEntry[] newTable = new HashEntry[oldCapacity << 1];
+ HashEntry<K,V>[] newTable = HashEntry.newArray(oldCapacity<<1);
+ threshold = (int)(newTable.length * loadFactor);
int sizeMask = newTable.length - 1;
for (int i = 0; i < oldCapacity ; i++) {
// We need to guarantee that any existing reads of old Map can
// proceed. So we cannot yet null out each bin.
- HashEntry<K,V> e = (HashEntry<K,V>)oldTable[i];
+ HashEntry<K,V> e = oldTable[i];
if (e != null) {
HashEntry<K,V> next = e.next;
@@ -436,15 +497,14 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
// Clone all remaining nodes
for (HashEntry<K,V> p = e; p != lastRun; p = p.next) {
int k = p.hash & sizeMask;
- newTable[k] = new HashEntry<K,V>(p.hash,
- p.key,
- p.value,
- (HashEntry<K,V>) newTable[k]);
+ HashEntry<K,V> n = newTable[k];
+ newTable[k] = new HashEntry<K,V>(p.key, p.hash,
+ n, p.value);
}
}
}
}
- return newTable;
+ table = newTable;
}
/**
@@ -453,33 +513,31 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
V remove(Object key, int hash, Object value) {
lock();
try {
- int c = count;
- HashEntry[] tab = table;
+ int c = count - 1;
+ HashEntry<K,V>[] tab = table;
int index = hash & (tab.length - 1);
- HashEntry<K,V> first = (HashEntry<K,V>)tab[index];
-
+ HashEntry<K,V> first = tab[index];
HashEntry<K,V> e = first;
- for (;;) {
- if (e == null)
- return null;
- if (e.hash == hash && key.equals(e.key))
- break;
+ while (e != null && (e.hash != hash || !key.equals(e.key)))
e = e.next;
- }
- V oldValue = e.value;
- if (value != null && !value.equals(oldValue))
- return null;
-
- // All entries following removed node can stay in list, but
- // all preceding ones need to be cloned.
- HashEntry<K,V> newFirst = e.next;
- for (HashEntry<K,V> p = first; p != e; p = p.next)
- newFirst = new HashEntry<K,V>(p.hash, p.key,
- p.value, newFirst);
- tab[index] = newFirst;
- ++modCount;
- count = c-1; // write-volatile
+ V oldValue = null;
+ if (e != null) {
+ V v = e.value;
+ if (value == null || value.equals(v)) {
+ oldValue = v;
+ // All entries following removed node can stay
+ // in list, but all preceding ones need to be
+ // cloned.
+ ++modCount;
+ HashEntry<K,V> newFirst = e.next;
+ for (HashEntry<K,V> p = first; p != e; p = p.next)
+ newFirst = new HashEntry<K,V>(p.key, p.hash,
+ newFirst, p.value);
+ tab[index] = newFirst;
+ count = c; // write-volatile
+ }
+ }
return oldValue;
} finally {
unlock();
@@ -487,50 +545,37 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
}
void clear() {
- lock();
- try {
- HashEntry[] tab = table;
- for (int i = 0; i < tab.length ; i++)
- tab[i] = null;
- ++modCount;
- count = 0; // write-volatile
- } finally {
- unlock();
+ if (count != 0) {
+ lock();
+ try {
+ HashEntry<K,V>[] tab = table;
+ for (int i = 0; i < tab.length ; i++)
+ tab[i] = null;
+ ++modCount;
+ count = 0; // write-volatile
+ } finally {
+ unlock();
+ }
}
}
}
- /**
- * ConcurrentHashMap list entry. Note that this is never exported
- * out as a user-visible Map.Entry
- */
- static final class HashEntry<K,V> {
- final K key;
- V value;
- final int hash;
- final HashEntry<K,V> next;
-
- HashEntry(int hash, K key, V value, HashEntry<K,V> next) {
- this.value = value;
- this.hash = hash;
- this.key = key;
- this.next = next;
- }
- }
/* ---------------- Public operations -------------- */
/**
* Creates a new, empty map with the specified initial
- * capacity and the specified load factor.
+ * capacity, load factor and concurrency level.
*
* @param initialCapacity the initial capacity. The implementation
* performs internal sizing to accommodate this many elements.
* @param loadFactor the load factor threshold, used to control resizing.
+ * Resizing may be performed when the average number of elements per
+ * bin exceeds this threshold.
* @param concurrencyLevel the estimated number of concurrently
* updating threads. The implementation performs internal sizing
- * to try to accommodate this many threads.
+ * to try to accommodate this many threads.
* @throws IllegalArgumentException if the initial capacity is
* negative or the load factor or concurrencyLevel are
* nonpositive.
@@ -552,7 +597,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
}
segmentShift = 32 - sshift;
segmentMask = ssize - 1;
- this.segments = new Segment[ssize];
+ this.segments = Segment.newArray(ssize);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
@@ -568,44 +613,50 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
}
/**
- * Creates a new, empty map with the specified initial
- * capacity, and with default load factor and concurrencyLevel.
+ * Creates a new, empty map with the specified initial capacity,
+ * and with default load factor (0.75) and concurrencyLevel (16).
*
- * @param initialCapacity The implementation performs internal
- * sizing to accommodate this many elements.
+ * @param initialCapacity the initial capacity. The implementation
+ * performs internal sizing to accommodate this many elements.
* @throws IllegalArgumentException if the initial capacity of
* elements is negative.
*/
public ConcurrentHashMap(int initialCapacity) {
- this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS);
+ this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
}
/**
- * Creates a new, empty map with a default initial capacity,
- * load factor, and concurrencyLevel.
+ * Creates a new, empty map with a default initial capacity (16),
+ * load factor (0.75) and concurrencyLevel (16).
*/
public ConcurrentHashMap() {
- this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS);
+ this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
}
/**
- * Creates a new map with the same mappings as the given map. The
- * map is created with a capacity of twice the number of mappings in
- * the given map or 11 (whichever is greater), and a default load factor.
- * @param t the map
+ * Creates a new map with the same mappings as the given map.
+ * The map is created with a capacity of 1.5 times the number
+ * of mappings in the given map or 16 (whichever is greater),
+ * and a default load factor (0.75) and concurrencyLevel (16).
+ *
+ * @param m the map
*/
- public ConcurrentHashMap(Map<? extends K, ? extends V> t) {
- this(Math.max((int) (t.size() / DEFAULT_LOAD_FACTOR) + 1,
- 11),
- DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS);
- putAll(t);
+ public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
+ this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
+ DEFAULT_INITIAL_CAPACITY),
+ DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
+ putAll(m);
}
- // inherit Map javadoc
+ /**
+ * Returns <tt>true</tt> if this map contains no key-value mappings.
+ *
+ * @return <tt>true</tt> if this map contains no key-value mappings
+ */
public boolean isEmpty() {
- final Segment[] segments = this.segments;
+ final Segment<K,V>[] segments = this.segments;
/*
- * We need to keep track of per-segment modCounts to avoid ABA
+ * We keep track of per-segment modCounts to avoid ABA
* problems in which an element in one segment was added and
* in another removed during traversal, in which case the
* table was never actually empty at any point. Note the
@@ -618,7 +669,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
for (int i = 0; i < segments.length; ++i) {
if (segments[i].count != 0)
return false;
- else
+ else
mcsum += mc[i] = segments[i].modCount;
}
// If mcsum happens to be zero, then we know we got a snapshot
@@ -627,25 +678,35 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
if (mcsum != 0) {
for (int i = 0; i < segments.length; ++i) {
if (segments[i].count != 0 ||
- mc[i] != segments[i].modCount)
+ mc[i] != segments[i].modCount)
return false;
}
}
return true;
}
- // inherit Map javadoc
+ /**
+ * Returns the number of key-value mappings in this map. If the
+ * map contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
+ * <tt>Integer.MAX_VALUE</tt>.
+ *
+ * @return the number of key-value mappings in this map
+ */
public int size() {
- final Segment[] segments = this.segments;
+ final Segment<K,V>[] segments = this.segments;
+ long sum = 0;
+ long check = 0;
int[] mc = new int[segments.length];
- for (;;) {
- long sum = 0;
+ // Try a few times to get accurate count. On failure due to
+ // continuous async changes in table, resort to locking.
+ for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
+ check = 0;
+ sum = 0;
int mcsum = 0;
for (int i = 0; i < segments.length; ++i) {
sum += segments[i].count;
mcsum += mc[i] = segments[i].modCount;
}
- int check = 0;
if (mcsum != 0) {
for (int i = 0; i < segments.length; ++i) {
check += segments[i].count;
@@ -655,43 +716,51 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
}
}
}
- if (check == sum) {
- if (sum > Integer.MAX_VALUE)
- return Integer.MAX_VALUE;
- else
- return (int)sum;
- }
+ if (check == sum)
+ break;
+ }
+ if (check != sum) { // Resort to locking all segments
+ sum = 0;
+ for (int i = 0; i < segments.length; ++i)
+ segments[i].lock();
+ for (int i = 0; i < segments.length; ++i)
+ sum += segments[i].count;
+ for (int i = 0; i < segments.length; ++i)
+ segments[i].unlock();
}
+ if (sum > Integer.MAX_VALUE)
+ return Integer.MAX_VALUE;
+ else
+ return (int)sum;
}
-
/**
- * Returns the value to which the specified key is mapped in this table.
+ * Returns the value to which the specified key is mapped,
+ * or {@code null} if this map contains no mapping for the key.
+ *
+ * <p>More formally, if this map contains a mapping from a key
+ * {@code k} to a value {@code v} such that {@code key.equals(k)},
+ * then this method returns {@code v}; otherwise it returns
+ * {@code null}. (There can be at most one such mapping.)
*
- * @param key a key in the table.
- * @return the value to which the key is mapped in this table;
- * <tt>null</tt> if the key is not mapped to any value in
- * this table.
- * @throws NullPointerException if the key is
- * <tt>null</tt>.
+ * @throws NullPointerException if the specified key is null
*/
public V get(Object key) {
- int hash = hash(key); // throws NullPointerException if key null
+ int hash = hash(key.hashCode());
return segmentFor(hash).get(key, hash);
}
/**
* Tests if the specified object is a key in this table.
*
- * @param key possible key.
- * @return <tt>true</tt> if and only if the specified object
- * is a key in this table, as determined by the
- * <tt>equals</tt> method; <tt>false</tt> otherwise.
- * @throws NullPointerException if the key is
- * <tt>null</tt>.
+ * @param key possible key
+ * @return <tt>true</tt> if and only if the specified object
+ * is a key in this table, as determined by the
+ * <tt>equals</tt> method; <tt>false</tt> otherwise.
+ * @throws NullPointerException if the specified key is null
*/
public boolean containsKey(Object key) {
- int hash = hash(key); // throws NullPointerException if key null
+ int hash = hash(key.hashCode());
return segmentFor(hash).containsKey(key, hash);
}
@@ -701,18 +770,22 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
* traversal of the hash table, and so is much slower than
* method <tt>containsKey</tt>.
*
- * @param value value whose presence in this map is to be tested.
+ * @param value value whose presence in this map is to be tested
* @return <tt>true</tt> if this map maps one or more keys to the
- * specified value.
- * @throws NullPointerException if the value is <tt>null</tt>.
+ * specified value
+ * @throws NullPointerException if the specified value is null
*/
public boolean containsValue(Object value) {
if (value == null)
throw new NullPointerException();
- final Segment[] segments = this.segments;
+ // See explanation of modCount use above
+
+ final Segment<K,V>[] segments = this.segments;
int[] mc = new int[segments.length];
- for (;;) {
+
+ // Try a few times without locking
+ for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
int sum = 0;
int mcsum = 0;
for (int i = 0; i < segments.length; ++i) {
@@ -734,287 +807,217 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
if (cleanSweep)
return false;
}
+ // Resort to locking all segments
+ for (int i = 0; i < segments.length; ++i)
+ segments[i].lock();
+ boolean found = false;
+ try {
+ for (int i = 0; i < segments.length; ++i) {
+ if (segments[i].containsValue(value)) {
+ found = true;
+ break;
+ }
+ }
+ } finally {
+ for (int i = 0; i < segments.length; ++i)
+ segments[i].unlock();
+ }
+ return found;
}
/**
* Legacy method testing if some key maps into the specified value
* in this table. This method is identical in functionality to
- * {@link #containsValue}, and exists solely to ensure
+ * {@link #containsValue}, and exists solely to ensure
* full compatibility with class {@link java.util.Hashtable},
* which supported this method prior to introduction of the
* Java Collections framework.
- * @param value a value to search for.
- * @return <tt>true</tt> if and only if some key maps to the
- * <tt>value</tt> argument in this table as
- * determined by the <tt>equals</tt> method;
- * <tt>false</tt> otherwise.
- * @throws NullPointerException if the value is <tt>null</tt>.
+ * @param value a value to search for
+ * @return <tt>true</tt> if and only if some key maps to the
+ * <tt>value</tt> argument in this table as
+ * determined by the <tt>equals</tt> method;
+ * <tt>false</tt> otherwise
+ * @throws NullPointerException if the specified value is null
*/
public boolean contains(Object value) {
return containsValue(value);
}
/**
- * Maps the specified <tt>key</tt> to the specified
- * <tt>value</tt> in this table. Neither the key nor the
- * value can be <tt>null</tt>.
+ * Maps the specified key to the specified value in this table.
+ * Neither the key nor the value can be null.
*
* <p> The value can be retrieved by calling the <tt>get</tt> method
* with a key that is equal to the original key.
*
- * @param key the table key.
- * @param value the value.
- * @return the previous value of the specified key in this table,
- * or <tt>null</tt> if it did not have one.
- * @throws NullPointerException if the key or value is
- * <tt>null</tt>.
+ * @param key key with which the specified value is to be associated
+ * @param value value to be associated with the specified key
+ * @return the previous value associated with <tt>key</tt>, or
+ * <tt>null</tt> if there was no mapping for <tt>key</tt>
+ * @throws NullPointerException if the specified key or value is null
*/
public V put(K key, V value) {
if (value == null)
throw new NullPointerException();
- int hash = hash(key);
+ int hash = hash(key.hashCode());
return segmentFor(hash).put(key, hash, value, false);
}
/**
- * If the specified key is not already associated
- * with a value, associate it with the given value.
- * This is equivalent to
- * <pre>
- * if (!map.containsKey(key))
- * return map.put(key, value);
- * else
- * return map.get(key);
- * </pre>
- * Except that the action is performed atomically.
- * @param key key with which the specified value is to be associated.
- * @param value value to be associated with the specified key.
- * @return previous value associated with specified key, or <tt>null</tt>
- * if there was no mapping for key. A <tt>null</tt> return can
- * also indicate that the map previously associated <tt>null</tt>
- * with the specified key, if the implementation supports
- * <tt>null</tt> values.
- *
- * @throws UnsupportedOperationException if the <tt>put</tt> operation is
- * not supported by this map.
- * @throws ClassCastException if the class of the specified key or value
- * prevents it from being stored in this map.
- * @throws NullPointerException if the specified key or value is
- * <tt>null</tt>.
+ * {@inheritDoc}
*
- **/
+ * @return the previous value associated with the specified key,
+ * or <tt>null</tt> if there was no mapping for the key
+ * @throws NullPointerException if the specified key or value is null
+ */
public V putIfAbsent(K key, V value) {
if (value == null)
throw new NullPointerException();
- int hash = hash(key);
+ int hash = hash(key.hashCode());
return segmentFor(hash).put(key, hash, value, true);
}
-
/**
* Copies all of the mappings from the specified map to this one.
- *
* These mappings replace any mappings that this map had for any of the
- * keys currently in the specified Map.
+ * keys currently in the specified map.
*
- * @param t Mappings to be stored in this map.
+ * @param m mappings to be stored in this map
*/
- public void putAll(Map<? extends K, ? extends V> t) {
- for (Iterator<? extends Map.Entry<? extends K, ? extends V>> it = (Iterator<? extends Map.Entry<? extends K, ? extends V>>) t.entrySet().iterator(); it.hasNext(); ) {
- Entry<? extends K, ? extends V> e = it.next();
+ public void putAll(Map<? extends K, ? extends V> m) {
+ for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
- }
}
/**
- * Removes the key (and its corresponding value) from this
- * table. This method does nothing if the key is not in the table.
+ * Removes the key (and its corresponding value) from this map.
+ * This method does nothing if the key is not in the map.
*
- * @param key the key that needs to be removed.
- * @return the value to which the key had been mapped in this table,
- * or <tt>null</tt> if the key did not have a mapping.
- * @throws NullPointerException if the key is
- * <tt>null</tt>.
+ * @param key the key that needs to be removed
+ * @return the previous value associated with <tt>key</tt>, or
+ * <tt>null</tt> if there was no mapping for <tt>key</tt>
+ * @throws NullPointerException if the specified key is null
*/
public V remove(Object key) {
- int hash = hash(key);
+ int hash = hash(key.hashCode());
return segmentFor(hash).remove(key, hash, null);
}
/**
- * Remove entry for key only if currently mapped to given value.
- * Acts as
- * <pre>
- * if (map.get(key).equals(value)) {
- * map.remove(key);
- * return true;
- * } else return false;
- * </pre>
- * except that the action is performed atomically.
- * @param key key with which the specified value is associated.
- * @param value value associated with the specified key.
- * @return true if the value was removed
- * @throws NullPointerException if the specified key is
- * <tt>null</tt>.
+ * {@inheritDoc}
+ *
+ * @throws NullPointerException if the specified key is null
*/
public boolean remove(Object key, Object value) {
- int hash = hash(key);
+ int hash = hash(key.hashCode());
+ if (value == null)
+ return false;
return segmentFor(hash).remove(key, hash, value) != null;
}
-
/**
- * Replace entry for key only if currently mapped to given value.
- * Acts as
- * <pre>
- * if (map.get(key).equals(oldValue)) {
- * map.put(key, newValue);
- * return true;
- * } else return false;
- * </pre>
- * except that the action is performed atomically.
- * @param key key with which the specified value is associated.
- * @param oldValue value expected to be associated with the specified key.
- * @param newValue value to be associated with the specified key.
- * @return true if the value was replaced
- * @throws NullPointerException if the specified key or values are
- * <tt>null</tt>.
+ * {@inheritDoc}
+ *
+ * @throws NullPointerException if any of the arguments are null
*/
public boolean replace(K key, V oldValue, V newValue) {
if (oldValue == null || newValue == null)
throw new NullPointerException();
- int hash = hash(key);
+ int hash = hash(key.hashCode());
return segmentFor(hash).replace(key, hash, oldValue, newValue);
}
/**
- * Replace entry for key only if currently mapped to some value.
- * Acts as
- * <pre>
- * if ((map.containsKey(key)) {
- * return map.put(key, value);
- * } else return null;
- * </pre>
- * except that the action is performed atomically.
- * @param key key with which the specified value is associated.
- * @param value value to be associated with the specified key.
- * @return previous value associated with specified key, or <tt>null</tt>
- * if there was no mapping for key.
- * @throws NullPointerException if the specified key or value is
- * <tt>null</tt>.
+ * {@inheritDoc}
+ *
+ * @return the previous value associated with the specified key,
+ * or <tt>null</tt> if there was no mapping for the key
+ * @throws NullPointerException if the specified key or value is null
*/
public V replace(K key, V value) {
if (value == null)
throw new NullPointerException();
- int hash = hash(key);
+ int hash = hash(key.hashCode());
return segmentFor(hash).replace(key, hash, value);
}
-
/**
- * Removes all mappings from this map.
+ * Removes all of the mappings from this map.
*/
public void clear() {
for (int i = 0; i < segments.length; ++i)
segments[i].clear();
}
-
- // BEGIN android-removed
- // /**
- // * Returns a shallow copy of this
- // * <tt>ConcurrentHashMap</tt> instance: the keys and
- // * values themselves are not cloned.
- // *
- // * @return a shallow copy of this map.
- // */
- // public Object clone() {
- // // We cannot call super.clone, since it would share final
- // // segments array, and there's no way to reassign finals.
- //
- // float lf = segments[0].loadFactor;
- // int segs = segments.length;
- // int cap = (int)(size() / lf);
- // if (cap < segs) cap = segs;
- // ConcurrentHashMap<K,V> t = new ConcurrentHashMap<K,V>(cap, lf, segs);
- // t.putAll(this);
- // return t;
- // }
- // END android-changed
-
/**
- * Returns a set view of the keys contained in this map. The set is
- * backed by the map, so changes to the map are reflected in the set, and
- * vice-versa. The set supports element removal, which removes the
- * corresponding mapping from this map, via the <tt>Iterator.remove</tt>,
- * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and
- * <tt>clear</tt> operations. It does not support the <tt>add</tt> or
+ * Returns a {@link Set} view of the keys contained in this map.
+ * The set is backed by the map, so changes to the map are
+ * reflected in the set, and vice-versa. The set supports element
+ * removal, which removes the corresponding mapping from this map,
+ * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
+ * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
+ * operations. It does not support the <tt>add</tt> or
* <tt>addAll</tt> operations.
- * The returned <tt>iterator</tt> is a "weakly consistent" iterator that
- * will never throw {@link java.util.ConcurrentModificationException},
+ *
+ * <p>The view's <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 a set view of the keys contained in this map.
*/
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null) ? ks : (keySet = new KeySet());
}
-
/**
- * Returns a collection view of the values contained in this map. The
- * collection is backed by the map, so changes to the map are reflected in
- * the collection, and vice-versa. The collection supports element
- * removal, which removes the corresponding mapping from this map, via the
- * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
- * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
- * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
- * The returned <tt>iterator</tt> is a "weakly consistent" iterator that
- * will never throw {@link java.util.ConcurrentModificationException},
+ * Returns a {@link Collection} view of the values contained in this map.
+ * The collection is backed by the map, so changes to the map are
+ * reflected in the collection, and vice-versa. The collection
+ * supports element removal, which removes the corresponding
+ * mapping from this map, via the <tt>Iterator.remove</tt>,
+ * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
+ * <tt>retainAll</tt>, and <tt>clear</tt> operations. It does not
+ * support the <tt>add</tt> or <tt>addAll</tt> operations.
+ *
+ * <p>The view's <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 a collection view of the values contained in this map.
*/
public Collection<V> values() {
Collection<V> vs = values;
return (vs != null) ? vs : (values = new Values());
}
-
/**
- * Returns a collection view of the mappings contained in this map. Each
- * element in the returned collection is a <tt>Map.Entry</tt>. The
- * collection is backed by the map, so changes to the map are reflected in
- * the collection, and vice-versa. The collection supports element
- * removal, which removes the corresponding mapping from the map, via the
- * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
- * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
- * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
- * The returned <tt>iterator</tt> is a "weakly consistent" iterator that
- * will never throw {@link java.util.ConcurrentModificationException},
+ * Returns a {@link Set} view of the mappings contained in this map.
+ * The set is backed by the map, so changes to the map are
+ * reflected in the set, and vice-versa. The set supports element
+ * removal, which removes the corresponding mapping from the map,
+ * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
+ * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
+ * operations. It does not support the <tt>add</tt> or
+ * <tt>addAll</tt> operations.
+ *
+ * <p>The view's <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 a collection view of the mappings contained in this map.
*/
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es = entrySet;
- return (es != null) ? es : (entrySet = (Set<Map.Entry<K,V>>) (Set) new EntrySet());
+ return (es != null) ? es : (entrySet = new EntrySet());
}
-
/**
* Returns an enumeration of the keys in this table.
*
- * @return an enumeration of the keys in this table.
- * @see #keySet
+ * @return an enumeration of the keys in this table
+ * @see #keySet()
*/
public Enumeration<K> keys() {
return new KeyIterator();
@@ -1023,8 +1026,8 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
/**
* Returns an enumeration of the values in this table.
*
- * @return an enumeration of the values in this table.
- * @see #values
+ * @return an enumeration of the values in this table
+ * @see #values()
*/
public Enumeration<V> elements() {
return new ValueIterator();
@@ -1035,7 +1038,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
abstract class HashIterator {
int nextSegmentIndex;
int nextTableIndex;
- HashEntry[] currentTable;
+ HashEntry<K,V>[] currentTable;
HashEntry<K, V> nextEntry;
HashEntry<K, V> lastReturned;
@@ -1052,16 +1055,16 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
return;
while (nextTableIndex >= 0) {
- if ( (nextEntry = (HashEntry<K,V>)currentTable[nextTableIndex--]) != null)
+ if ( (nextEntry = currentTable[nextTableIndex--]) != null)
return;
}
while (nextSegmentIndex >= 0) {
- Segment<K,V> seg = (Segment<K,V>)segments[nextSegmentIndex--];
+ Segment<K,V> seg = segments[nextSegmentIndex--];
if (seg.count != 0) {
currentTable = seg.table;
for (int j = currentTable.length - 1; j >= 0; --j) {
- if ( (nextEntry = (HashEntry<K,V>)currentTable[j]) != null) {
+ if ( (nextEntry = currentTable[j]) != null) {
nextTableIndex = j - 1;
return;
}
@@ -1088,81 +1091,58 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
}
}
- final class KeyIterator extends HashIterator implements Iterator<K>, Enumeration<K> {
- public K next() { return super.nextEntry().key; }
+ final class KeyIterator
+ extends HashIterator
+ implements Iterator<K>, Enumeration<K>
+ {
+ public K next() { return super.nextEntry().key; }
public K nextElement() { return super.nextEntry().key; }
}
- final class ValueIterator extends HashIterator implements Iterator<V>, Enumeration<V> {
- public V next() { return super.nextEntry().value; }
+ final class ValueIterator
+ extends HashIterator
+ implements Iterator<V>, Enumeration<V>
+ {
+ public V next() { return super.nextEntry().value; }
public V nextElement() { return super.nextEntry().value; }
}
-
-
/**
- * Entry iterator. Exported Entry objects must write-through
- * changes in setValue, even if the nodes have been cloned. So we
- * cannot return internal HashEntry objects. Instead, the iterator
- * itself acts as a forwarding pseudo-entry.
+ * Custom Entry class used by EntryIterator.next(), that relays
+ * setValue changes to the underlying map.
*/
- final class EntryIterator extends HashIterator implements Map.Entry<K,V>, Iterator<Entry<K,V>> {
- public Map.Entry<K,V> next() {
- nextEntry();
- return this;
- }
-
- public K getKey() {
- if (lastReturned == null)
- throw new IllegalStateException("Entry was removed");
- return lastReturned.key;
- }
-
- public V getValue() {
- if (lastReturned == null)
- throw new IllegalStateException("Entry was removed");
- return ConcurrentHashMap.this.get(lastReturned.key);
+ final class WriteThroughEntry
+ extends SimpleEntry<K,V>
+ {
+ WriteThroughEntry(K k, V v) {
+ super(k,v);
}
+ /**
+ * Set our entry's value and write through to the map. The
+ * value to return is somewhat arbitrary here. Since a
+ * WriteThroughEntry does not necessarily track asynchronous
+ * changes, the most recent "previous" value could be
+ * different from what we return (or could even have been
+ * removed in which case the put will re-establish). We do not
+ * and cannot guarantee more.
+ */
public V setValue(V value) {
- if (lastReturned == null)
- throw new IllegalStateException("Entry was removed");
- return ConcurrentHashMap.this.put(lastReturned.key, value);
- }
-
- public boolean equals(Object o) {
- // If not acting as entry, just use default.
- if (lastReturned == null)
- return super.equals(o);
- if (!(o instanceof Map.Entry))
- return false;
- Map.Entry e = (Map.Entry)o;
- return eq(getKey(), e.getKey()) && eq(getValue(), e.getValue());
- }
-
- public int hashCode() {
- // If not acting as entry, just use default.
- if (lastReturned == null)
- return super.hashCode();
-
- Object k = getKey();
- Object v = getValue();
- return ((k == null) ? 0 : k.hashCode()) ^
- ((v == null) ? 0 : v.hashCode());
- }
-
- public String toString() {
- // If not acting as entry, just use default.
- if (lastReturned == null)
- return super.toString();
- else
- return getKey() + "=" + getValue();
+ if (value == null) throw new NullPointerException();
+ V v = super.setValue(value);
+ ConcurrentHashMap.this.put(getKey(), value);
+ return v;
}
+ }
- boolean eq(Object o1, Object o2) {
- return (o1 == null ? o2 == null : o1.equals(o2));
+ final class EntryIterator
+ extends HashIterator
+ implements Iterator<Entry<K,V>>
+ {
+ public Map.Entry<K,V> next() {
+ HashEntry<K,V> e = super.nextEntry();
+ return new WriteThroughEntry(e.key, e.value);
}
-
}
final class KeySet extends AbstractSet<K> {
@@ -1172,6 +1152,9 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
public int size() {
return ConcurrentHashMap.this.size();
}
+ public boolean isEmpty() {
+ return ConcurrentHashMap.this.isEmpty();
+ }
public boolean contains(Object o) {
return ConcurrentHashMap.this.containsKey(o);
}
@@ -1190,6 +1173,9 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
public int size() {
return ConcurrentHashMap.this.size();
}
+ public boolean isEmpty() {
+ return ConcurrentHashMap.this.isEmpty();
+ }
public boolean contains(Object o) {
return ConcurrentHashMap.this.containsValue(o);
}
@@ -1205,44 +1191,32 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
- Map.Entry<K,V> e = (Map.Entry<K,V>)o;
+ Map.Entry<?,?> e = (Map.Entry<?,?>)o;
V v = ConcurrentHashMap.this.get(e.getKey());
return v != null && v.equals(e.getValue());
}
public boolean remove(Object o) {
if (!(o instanceof Map.Entry))
return false;
- Map.Entry<K,V> e = (Map.Entry<K,V>)o;
+ Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return ConcurrentHashMap.this.remove(e.getKey(), e.getValue());
}
public int size() {
return ConcurrentHashMap.this.size();
}
+ public boolean isEmpty() {
+ return ConcurrentHashMap.this.isEmpty();
+ }
public void clear() {
ConcurrentHashMap.this.clear();
}
- public Object[] toArray() {
- // Since we don't ordinarily have distinct Entry objects, we
- // must pack elements using exportable SimpleEntry
- Collection<Map.Entry<K,V>> c = new ArrayList<Map.Entry<K,V>>(size());
- for (Iterator<Map.Entry<K,V>> i = iterator(); i.hasNext(); )
- c.add(new SimpleEntry<K,V>(i.next()));
- return c.toArray();
- }
- public <T> T[] toArray(T[] a) {
- Collection<Map.Entry<K,V>> c = new ArrayList<Map.Entry<K,V>>(size());
- for (Iterator<Map.Entry<K,V>> i = iterator(); i.hasNext(); )
- c.add(new SimpleEntry<K,V>(i.next()));
- return c.toArray(a);
- }
-
}
/**
* This duplicates java.util.AbstractMap.SimpleEntry until this class
* is made accessible.
*/
- static final class SimpleEntry<K,V> implements Entry<K,V> {
+ static class SimpleEntry<K,V> implements Entry<K,V> {
K key;
V value;
@@ -1279,7 +1253,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
public int hashCode() {
return ((key == null) ? 0 : key.hashCode()) ^
- ((value == null) ? 0 : value.hashCode());
+ ((value == null) ? 0 : value.hashCode());
}
public String toString() {
@@ -1291,12 +1265,11 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
}
}
- /* ---------------- Serialization Support -------------- */
+ /* ---------------- Serialization Support -------------- */
/**
- * Save the state of the <tt>ConcurrentHashMap</tt>
- * instance to a stream (i.e.,
- * serialize it).
+ * Save the state of the <tt>ConcurrentHashMap</tt> instance to a
+ * stream (i.e., serialize it).
* @param s the stream
* @serialData
* the key (Object) and value (Object)
@@ -1307,12 +1280,12 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
s.defaultWriteObject();
for (int k = 0; k < segments.length; ++k) {
- Segment<K,V> seg = (Segment<K,V>)segments[k];
+ Segment<K,V> seg = segments[k];
seg.lock();
try {
- HashEntry[] tab = seg.table;
+ HashEntry<K,V>[] tab = seg.table;
for (int i = 0; i < tab.length; ++i) {
- for (HashEntry<K,V> e = (HashEntry<K,V>)tab[i]; e != null; e = e.next) {
+ for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) {
s.writeObject(e.key);
s.writeObject(e.value);
}
@@ -1326,9 +1299,8 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
}
/**
- * Reconstitute the <tt>ConcurrentHashMap</tt>
- * instance from a stream (i.e.,
- * deserialize it).
+ * Reconstitute the <tt>ConcurrentHashMap</tt> instance from a
+ * stream (i.e., deserialize it).
* @param s the stream
*/
private void readObject(java.io.ObjectInputStream s)
@@ -1350,4 +1322,3 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
}
}
}
-
diff --git a/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java b/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
index 794142d..2253823 100644
--- a/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
+++ b/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
@@ -14,7 +14,7 @@ import java.util.concurrent.atomic.*;
// END android-note
/**
- * An unbounded thread-safe {@linkplain Queue queue} based on linked nodes.
+ * An unbounded thread-safe {@linkplain Queue queue} based on linked nodes.
* 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.
@@ -22,23 +22,31 @@ import java.util.concurrent.atomic.*;
* 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.
- * A <tt>ConcurrentLinkedQueue</tt> is an appropriate choice when
+ * A {@code ConcurrentLinkedQueue} is an appropriate choice when
* many threads will share access to a common collection.
- * This queue does not permit <tt>null</tt> elements.
+ * This queue does not permit {@code null} elements.
*
- * <p>This implementation employs an efficient &quot;wait-free&quot;
+ * <p>This implementation employs an efficient &quot;wait-free&quot;
* algorithm based on one described in <a
* href="http://www.cs.rochester.edu/u/michael/PODC96.html"> Simple,
* Fast, and Practical Non-Blocking and Blocking Concurrent Queue
* Algorithms</a> by Maged M. Michael and Michael L. Scott.
*
- * <p>Beware that, unlike in most collections, the <tt>size</tt> method
+ * <p>Beware that, unlike in most collections, the {@code size} method
* is <em>NOT</em> a constant-time operation. Because of the
* asynchronous nature of these queues, determining the current number
* of elements requires a traversal of the elements.
*
- * <p>This class implements all of the <em>optional</em> methods
- * of the {@link Collection} and {@link Iterator} interfaces.
+ * <p>This class and its iterator implement all of the
+ * <em>optional</em> methods of the {@link Collection} and {@link
+ * Iterator} interfaces.
+ *
+ * <p>Memory consistency effects: As with other concurrent
+ * collections, actions in a thread prior to placing an object into a
+ * {@code ConcurrentLinkedQueue}
+ * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
+ * actions subsequent to the access or removal of that element from
+ * the {@code ConcurrentLinkedQueue} in another thread.
*
* @since 1.5
* @author Doug Lea
@@ -50,68 +58,126 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
private static final long serialVersionUID = 196745693267521676L;
/*
- * This is a straight adaptation of Michael & Scott algorithm.
- * For explanation, read the paper. The only (minor) algorithmic
- * difference is that this version supports lazy deletion of
- * internal nodes (method remove(Object)) -- remove CAS'es item
- * fields to null. The normal queue operations unlink but then
- * pass over nodes with null item fields. Similarly, iteration
- * methods ignore those with nulls.
+ * This is a modification of the Michael & Scott algorithm,
+ * adapted for a garbage-collected environment, with support for
+ * interior node deletion (to support remove(Object)). For
+ * explanation, read the paper.
+ *
+ * Note that like most non-blocking algorithms in this package,
+ * this implementation relies on the fact that in garbage
+ * collected systems, there is no possibility of ABA problems due
+ * to recycled nodes, so there is no need to use "counted
+ * pointers" or related techniques seen in versions used in
+ * non-GC'ed settings.
+ *
+ * The fundamental invariants are:
+ * - There is exactly one (last) Node with a null next reference,
+ * which is CASed when enqueueing. This last Node can be
+ * reached in O(1) time from tail, but tail is merely an
+ * optimization - it can always be reached in O(N) time from
+ * head as well.
+ * - The elements contained in the queue are the non-null items in
+ * Nodes that are reachable from head. CASing the item
+ * reference of a Node to null atomically removes it from the
+ * queue. Reachability of all elements from head must remain
+ * true even in the case of concurrent modifications that cause
+ * head to advance. A dequeued Node may remain in use
+ * indefinitely due to creation of an Iterator or simply a
+ * poll() that has lost its time slice.
+ *
+ * The above might appear to imply that all Nodes are GC-reachable
+ * from a predecessor dequeued Node. That would cause two problems:
+ * - allow a rogue Iterator to cause unbounded memory retention
+ * - cause cross-generational linking of old Nodes to new Nodes if
+ * a Node was tenured while live, which generational GCs have a
+ * hard time dealing with, causing repeated major collections.
+ * However, only non-deleted Nodes need to be reachable from
+ * dequeued Nodes, and reachability does not necessarily have to
+ * be of the kind understood by the GC. We use the trick of
+ * linking a Node that has just been dequeued to itself. Such a
+ * self-link implicitly means to advance to head.
+ *
+ * Both head and tail are permitted to lag. In fact, failing to
+ * update them every time one could is a significant optimization
+ * (fewer CASes). This is controlled by local "hops" variables
+ * that only trigger helping-CASes after experiencing multiple
+ * lags.
+ *
+ * Since head and tail are updated concurrently and independently,
+ * it is possible for tail to lag behind head (why not)?
+ *
+ * CASing a Node's item reference to null atomically removes the
+ * element from the queue. Iterators skip over Nodes with null
+ * items. Prior implementations of this class had a race between
+ * poll() and remove(Object) where the same element would appear
+ * to be successfully removed by two concurrent operations. The
+ * method remove(Object) also lazily unlinks deleted Nodes, but
+ * this is merely an optimization.
+ *
+ * When constructing a Node (before enqueuing it) we avoid paying
+ * for a volatile write to item by using lazySet instead of a
+ * normal write. This allows the cost of enqueue to be
+ * "one-and-a-half" CASes.
+ *
+ * Both head and tail may or may not point to a Node with a
+ * non-null item. If the queue is empty, all items must of course
+ * be null. Upon creation, both head and tail refer to a dummy
+ * Node with null item. Both head and tail are only updated using
+ * CAS, so they never regress, although again this is merely an
+ * optimization.
*/
-
private static class Node<E> {
private volatile E item;
private volatile Node<E> next;
-
- private static final
- AtomicReferenceFieldUpdater<Node, Node>
+
+ private static final
+ AtomicReferenceFieldUpdater<Node, Node>
nextUpdater =
AtomicReferenceFieldUpdater.newUpdater
(Node.class, Node.class, "next");
- private static final
- AtomicReferenceFieldUpdater<Node, Object>
+ private static final
+ AtomicReferenceFieldUpdater<Node, Object>
itemUpdater =
AtomicReferenceFieldUpdater.newUpdater
(Node.class, Object.class, "item");
-
- Node(E x) { item = x; }
-
- Node(E x, Node<E> n) { item = x; next = n; }
-
+
+
+ Node(E item) { setItem(item); }
+
E getItem() {
return item;
}
-
+
boolean casItem(E cmp, E val) {
return itemUpdater.compareAndSet(this, cmp, val);
}
-
+
void setItem(E val) {
itemUpdater.set(this, val);
}
-
+
Node<E> getNext() {
return next;
}
-
+
boolean casNext(Node<E> cmp, Node<E> val) {
return nextUpdater.compareAndSet(this, cmp, val);
}
-
+
void setNext(Node<E> val) {
nextUpdater.set(this, val);
- }
-
}
- private static final
- AtomicReferenceFieldUpdater<ConcurrentLinkedQueue, Node>
- tailUpdater =
+ }
+
+ private static final
+ AtomicReferenceFieldUpdater<ConcurrentLinkedQueue, Node>
+ tailUpdater =
AtomicReferenceFieldUpdater.newUpdater
(ConcurrentLinkedQueue.class, Node.class, "tail");
- private static final
- AtomicReferenceFieldUpdater<ConcurrentLinkedQueue, Node>
- headUpdater =
+ private static final
+ AtomicReferenceFieldUpdater<ConcurrentLinkedQueue, Node>
+ headUpdater =
AtomicReferenceFieldUpdater.newUpdater
(ConcurrentLinkedQueue.class, Node.class, "head");
@@ -124,118 +190,141 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
}
+
/**
- * Pointer to header node, initialized to a dummy node. The first
- * actual node is at head.getNext().
+ * Pointer to first node, initialized to a dummy node.
*/
- private transient volatile Node<E> head = new Node<E>(null, null);
+ private transient volatile Node<E> head = new Node<E>(null);
- /** Pointer to last node on list **/
+ /** Pointer to last node on list */
private transient volatile Node<E> tail = head;
/**
- * Creates a <tt>ConcurrentLinkedQueue</tt> that is initially empty.
+ * Creates a {@code ConcurrentLinkedQueue} that is initially empty.
*/
public ConcurrentLinkedQueue() {}
/**
- * Creates a <tt>ConcurrentLinkedQueue</tt>
+ * Creates a {@code ConcurrentLinkedQueue}
* initially containing the elements of the given collection,
* added in traversal order of the collection's iterator.
* @param c the collection of elements to initially contain
- * @throws NullPointerException if <tt>c</tt> or any element within it
- * is <tt>null</tt>
+ * @throws NullPointerException if the specified collection or any
+ * of its elements are null
*/
public ConcurrentLinkedQueue(Collection<? extends E> c) {
for (Iterator<? extends E> it = c.iterator(); it.hasNext();)
add(it.next());
}
- // Have to override just to update the javadoc
+ // Have to override just to update the javadoc
/**
- * Adds the specified element to the tail of this queue.
- * @param o the element to add.
- * @return <tt>true</tt> (as per the general contract of
- * <tt>Collection.add</tt>).
+ * Inserts the specified element at the tail of this queue.
*
- * @throws NullPointerException if the specified element is <tt>null</tt>
+ * @return {@code true} (as specified by {@link Collection#add})
+ * @throws NullPointerException if the specified element is null
*/
- public boolean add(E o) {
- return offer(o);
+ public boolean add(E e) {
+ return offer(e);
}
/**
- * Inserts the specified element to the tail of this queue.
+ * We don't bother to update head or tail pointers if less than
+ * HOPS links from "true" location. We assume that volatile
+ * writes are significantly more expensive than volatile reads.
+ */
+ private static final int HOPS = 1;
+
+ /**
+ * Try to CAS head to p. If successful, repoint old head to itself
+ * as sentinel for succ(), below.
+ */
+ final void updateHead(Node<E> h, Node<E> p) {
+ if (h != p && casHead(h, p))
+ h.setNext(h);
+ }
+
+ /**
+ * Returns the successor of p, or the head node if p.next has been
+ * linked to self, which will only be true if traversing with a
+ * stale pointer that is now off the list.
+ */
+ final Node<E> succ(Node<E> p) {
+ Node<E> next = p.getNext();
+ return (p == next) ? head : next;
+ }
+
+ /**
+ * Inserts the specified element at the tail of this queue.
*
- * @param o the element to add.
- * @return <tt>true</tt> (as per the general contract of
- * <tt>Queue.offer</tt>).
- * @throws NullPointerException if the specified element is <tt>null</tt>
+ * @return {@code true} (as specified by {@link Queue#offer})
+ * @throws NullPointerException if the specified element is null
*/
- public boolean offer(E o) {
- if (o == null) throw new NullPointerException();
- Node<E> n = new Node<E>(o, null);
- for(;;) {
+ public boolean offer(E e) {
+ if (e == null) throw new NullPointerException();
+ Node<E> n = new Node<E>(e);
+ retry:
+ for (;;) {
Node<E> t = tail;
- Node<E> s = t.getNext();
- if (t == tail) {
- if (s == null) {
- if (t.casNext(s, n)) {
- casTail(t, n);
- return true;
- }
+ Node<E> p = t;
+ for (int hops = 0; ; hops++) {
+ Node<E> next = succ(p);
+ if (next != null) {
+ if (hops > HOPS && t != tail)
+ continue retry;
+ p = next;
+ } else if (p.casNext(null, n)) {
+ if (hops >= HOPS)
+ casTail(t, n); // Failure is OK.
+ return true;
} else {
- casTail(t, s);
+ p = succ(p);
}
}
}
}
public E poll() {
- for (;;) {
- Node<E> h = head;
- Node<E> t = tail;
- Node<E> first = h.getNext();
- if (h == head) {
- if (h == t) {
- if (first == null)
- return null;
- else
- casTail(t, first);
- } else if (casHead(h, first)) {
- E item = first.getItem();
- if (item != null) {
- first.setItem(null);
- return item;
- }
- // else skip over deleted item, continue loop,
+ Node<E> h = head;
+ Node<E> p = h;
+ for (int hops = 0; ; hops++) {
+ E item = p.getItem();
+
+ if (item != null && p.casItem(item, null)) {
+ if (hops >= HOPS) {
+ Node<E> q = p.getNext();
+ updateHead(h, (q != null) ? q : p);
}
+ return item;
}
+ Node<E> next = succ(p);
+ if (next == null) {
+ updateHead(h, p);
+ break;
+ }
+ p = next;
}
+ return null;
}
- public E peek() { // same as poll except don't remove item
+ public E peek() {
+ Node<E> h = head;
+ Node<E> p = h;
+ E item;
for (;;) {
- Node<E> h = head;
- Node<E> t = tail;
- Node<E> first = h.getNext();
- if (h == head) {
- if (h == t) {
- if (first == null)
- return null;
- else
- casTail(t, first);
- } else {
- E item = first.getItem();
- if (item != null)
- return item;
- else // remove deleted node and continue
- casHead(h, first);
- }
+ item = p.getItem();
+ if (item != null)
+ break;
+ Node<E> next = succ(p);
+ if (next == null) {
+ break;
}
+ p = next;
}
+ updateHead(h, p);
+ return item;
}
/**
@@ -245,46 +334,50 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
* introducing race.)
*/
Node<E> first() {
+ Node<E> h = head;
+ Node<E> p = h;
+ Node<E> result;
for (;;) {
- Node<E> h = head;
- Node<E> t = tail;
- Node<E> first = h.getNext();
- if (h == head) {
- if (h == t) {
- if (first == null)
- return null;
- else
- casTail(t, first);
- } else {
- if (first.getItem() != null)
- return first;
- else // remove deleted node and continue
- casHead(h, first);
- }
+ E item = p.getItem();
+ if (item != null) {
+ result = p;
+ break;
}
+ Node<E> next = succ(p);
+ if (next == null) {
+ result = null;
+ break;
+ }
+ p = next;
}
+ updateHead(h, p);
+ return result;
}
-
+ /**
+ * Returns {@code true} if this queue contains no elements.
+ *
+ * @return {@code true} if this queue contains no elements
+ */
public boolean isEmpty() {
return first() == null;
}
/**
* Returns the number of elements in this queue. If this queue
- * contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
- * <tt>Integer.MAX_VALUE</tt>.
+ * contains more than {@code Integer.MAX_VALUE} elements, returns
+ * {@code Integer.MAX_VALUE}.
*
* <p>Beware that, unlike in most collections, this method is
* <em>NOT</em> a constant-time operation. Because of the
* asynchronous nature of these queues, determining the current
* number of elements requires an O(n) traversal.
*
- * @return the number of elements in this queue.
+ * @return the number of elements in this queue
*/
public int size() {
int count = 0;
- for (Node<E> p = first(); p != null; p = p.getNext()) {
+ for (Node<E> p = first(); p != null; p = succ(p)) {
if (p.getItem() != null) {
// Collections.size() spec says to max out
if (++count == Integer.MAX_VALUE)
@@ -294,9 +387,17 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
return count;
}
+ /**
+ * Returns {@code true} if this queue contains the specified element.
+ * More formally, returns {@code true} if and only if this queue contains
+ * at least one element {@code e} such that {@code o.equals(e)}.
+ *
+ * @param o object to be checked for containment in this queue
+ * @return {@code true} if this queue contains the specified element
+ */
public boolean contains(Object o) {
if (o == null) return false;
- for (Node<E> p = first(); p != null; p = p.getNext()) {
+ for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.getItem();
if (item != null &&
o.equals(item))
@@ -305,22 +406,50 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
return false;
}
+ /**
+ * Removes a single instance of the specified element from this queue,
+ * if it is present. More formally, removes an element {@code e} such
+ * that {@code o.equals(e)}, if this queue contains one or more such
+ * elements.
+ * Returns {@code true} 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 {@code true} if this queue changed as a result of the call
+ */
public boolean remove(Object o) {
if (o == null) return false;
- for (Node<E> p = first(); p != null; p = p.getNext()) {
+ Node<E> pred = null;
+ for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.getItem();
- if (item != null &&
- o.equals(item) &&
- p.casItem(item, null))
+ if (item != null && o.equals(item) && p.casItem(item, null)) {
+ Node<E> next = succ(p);
+ if (pred != null && next != null)
+ pred.casNext(p, next);
return true;
+ }
+ pred = p;
}
return false;
}
+ /**
+ * 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() {
// Use ArrayList to deal with resizing.
ArrayList<E> al = new ArrayList<E>();
- for (Node<E> p = first(); p != null; p = p.getNext()) {
+ for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.getItem();
if (item != null)
al.add(item);
@@ -328,11 +457,48 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
return al.toArray();
}
+ /**
+ * 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
+ * {@code null}.
+ *
+ * <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 {@code x} is a queue known to contain only strings.
+ * The following code can be used to dump the queue into a newly
+ * allocated array of {@code String}:
+ *
+ * <pre>
+ * String[] y = x.toArray(new String[0]);</pre>
+ *
+ * Note that {@code toArray(new Object[0])} is identical in function to
+ * {@code toArray()}.
+ *
+ * @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
+ */
+ @SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
// try to use sent-in array
int k = 0;
Node<E> p;
- for (p = first(); p != null && k < a.length; p = p.getNext()) {
+ for (p = first(); p != null && k < a.length; p = succ(p)) {
E item = p.getItem();
if (item != null)
a[k++] = (T)item;
@@ -345,23 +511,23 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
// If won't fit, use ArrayList version
ArrayList<E> al = new ArrayList<E>();
- for (Node<E> q = first(); q != null; q = q.getNext()) {
+ for (Node<E> q = first(); q != null; q = succ(q)) {
E item = q.getItem();
if (item != null)
al.add(item);
}
- return (T[])al.toArray(a);
+ return al.toArray(a);
}
/**
* Returns an iterator over the elements in this queue in proper sequence.
* The returned iterator is a "weakly consistent" iterator that
- * will never throw {@link java.util.ConcurrentModificationException},
+ * 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.
+ * @return an iterator over the elements in this queue in proper sequence
*/
public Iterator<E> iterator() {
return new Itr();
@@ -378,7 +544,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
* 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;
/**
@@ -398,7 +564,15 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
lastRet = nextNode;
E x = nextItem;
- Node<E> p = (nextNode == null)? first() : nextNode.getNext();
+ Node<E> pred, p;
+ if (nextNode == null) {
+ p = first();
+ pred = null;
+ } else {
+ pred = nextNode;
+ p = succ(nextNode);
+ }
+
for (;;) {
if (p == null) {
nextNode = null;
@@ -410,8 +584,13 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
nextNode = p;
nextItem = item;
return x;
- } else // skip over nulls
- p = p.getNext();
+ } else {
+ // skip over nulls
+ Node<E> next = succ(p);
+ if (pred != null && next != null)
+ pred.casNext(p, next);
+ p = next;
+ }
}
}
@@ -436,7 +615,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
/**
* Save the state to a stream (that is, serialize it).
*
- * @serialData All of the elements (each an <tt>E</tt>) in
+ * @serialData All of the elements (each an {@code E}) in
* the proper order, followed by a null
* @param s the stream
*/
@@ -447,7 +626,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
s.defaultWriteObject();
// Write out all elements in the proper order.
- for (Node<E> p = first(); p != null; p = p.getNext()) {
+ for (Node<E> p = first(); p != null; p = succ(p)) {
Object item = p.getItem();
if (item != null)
s.writeObject(item);
@@ -466,10 +645,11 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
throws java.io.IOException, ClassNotFoundException {
// Read in capacity, and any hidden stuff
s.defaultReadObject();
- head = new Node<E>(null, null);
+ head = new Node<E>(null);
tail = head;
// Read in all elements and place in queue
for (;;) {
+ @SuppressWarnings("unchecked")
E item = (E)s.readObject();
if (item == null)
break;
diff --git a/concurrent/src/main/java/java/util/concurrent/ConcurrentMap.java b/concurrent/src/main/java/java/util/concurrent/ConcurrentMap.java
index 32dc000..2daebc5 100644
--- a/concurrent/src/main/java/java/util/concurrent/ConcurrentMap.java
+++ b/concurrent/src/main/java/java/util/concurrent/ConcurrentMap.java
@@ -15,10 +15,17 @@ import java.util.Map;
* A {@link java.util.Map} providing additional atomic
* <tt>putIfAbsent</tt>, <tt>remove</tt>, and <tt>replace</tt> methods.
*
+ * <p>Memory consistency effects: As with other concurrent
+ * collections, actions in a thread prior to placing an object into a
+ * {@code ConcurrentMap} as a key or value
+ * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
+ * actions subsequent to the access or removal of that object from
+ * the {@code ConcurrentMap} in another thread.
+ *
* @since 1.5
* @author Doug Lea
* @param <K> the type of keys maintained by this map
- * @param <V> the type of mapped values
+ * @param <V> the type of mapped values
*/
public interface ConcurrentMap<K, V> extends Map<K, V> {
/**
@@ -26,93 +33,102 @@ public interface ConcurrentMap<K, V> extends Map<K, V> {
* with a value, associate it with the given value.
* This is equivalent to
* <pre>
- * if (!map.containsKey(key))
- * return map.put(key, value);
+ * if (!map.containsKey(key))
+ * return map.put(key, value);
* else
- * return map.get(key);
- * </pre>
- * Except that the action is performed atomically.
- * @param key key with which the specified value is to be associated.
- * @param value value to be associated with the specified key.
- * @return previous value associated with specified key, or <tt>null</tt>
- * if there was no mapping for key. A <tt>null</tt> return can
- * also indicate that the map previously associated <tt>null</tt>
- * with the specified key, if the implementation supports
- * <tt>null</tt> values.
+ * return map.get(key);</pre>
+ * except that the action is performed atomically.
*
- * @throws UnsupportedOperationException if the <tt>put</tt> operation is
- * not supported by this map.
+ * @param key key with which the specified value is to be associated
+ * @param value value to be associated with the specified key
+ * @return the previous value associated with the specified key, or
+ * <tt>null</tt> if there was no mapping for the key.
+ * (A <tt>null</tt> return can also indicate that the map
+ * previously associated <tt>null</tt> with the key,
+ * if the implementation supports null values.)
+ * @throws UnsupportedOperationException if the <tt>put</tt> operation
+ * is not supported by this map
* @throws ClassCastException if the class of the specified key or value
- * prevents it from being stored in this map.
- * @throws IllegalArgumentException if some aspect of this key or value
- * prevents it from being stored in this map.
- * @throws NullPointerException if this map does not permit <tt>null</tt>
- * keys or values, and the specified key or value is
- * <tt>null</tt>.
+ * prevents it from being stored in this map
+ * @throws NullPointerException if the specified key or value is null,
+ * and this map does not permit null keys or values
+ * @throws IllegalArgumentException if some property of the specified key
+ * or value prevents it from being stored in this map
*
*/
V putIfAbsent(K key, V value);
/**
- * Remove entry for key only if currently mapped to given value.
- * Acts as
- * <pre>
- * if ((map.containsKey(key) && map.get(key).equals(value)) {
- * map.remove(key);
- * return true;
- * } else return false;
- * </pre>
+ * Removes the entry for a key only if currently mapped to a given value.
+ * This is equivalent to
+ * <pre>
+ * if (map.containsKey(key) &amp;&amp; map.get(key).equals(value)) {
+ * map.remove(key);
+ * return true;
+ * } else return false;</pre>
* except that the action is performed atomically.
- * @param key key with which the specified value is associated.
- * @param value value associated with the specified key.
- * @return true if the value was removed, false otherwise
- * @throws NullPointerException if this map does not permit <tt>null</tt>
- * keys or values, and the specified key or value is
- * <tt>null</tt>.
+ *
+ * @param key key with which the specified value is associated
+ * @param value value expected to be associated with the specified key
+ * @return <tt>true</tt> if the value was removed
+ * @throws UnsupportedOperationException if the <tt>remove</tt> operation
+ * is not supported by this map
+ * @throws ClassCastException if the key or value is of an inappropriate
+ * type for this map (optional)
+ * @throws NullPointerException if the specified key or value is null,
+ * and this map does not permit null keys or values (optional)
*/
boolean remove(Object key, Object value);
-
/**
- * Replace entry for key only if currently mapped to given value.
- * Acts as
- * <pre>
- * if ((map.containsKey(key) && map.get(key).equals(oldValue)) {
- * map.put(key, newValue);
- * return true;
- * } else return false;
- * </pre>
+ * Replaces the entry for a key only if currently mapped to a given value.
+ * This is equivalent to
+ * <pre>
+ * if (map.containsKey(key) &amp;&amp; map.get(key).equals(oldValue)) {
+ * map.put(key, newValue);
+ * return true;
+ * } else return false;</pre>
* except that the action is performed atomically.
- * @param key key with which the specified value is associated.
- * @param oldValue value expected to be associated with the specified key.
- * @param newValue value to be associated with the specified key.
- * @return true if the value was replaced
- * @throws NullPointerException if this map does not permit <tt>null</tt>
- * keys or values, and the specified key or value is
- * <tt>null</tt>.
+ *
+ * @param key key with which the specified value is associated
+ * @param oldValue value expected to be associated with the specified key
+ * @param newValue value to be associated with the specified key
+ * @return <tt>true</tt> if the value was replaced
+ * @throws UnsupportedOperationException if the <tt>put</tt> operation
+ * is not supported by this map
+ * @throws ClassCastException if the class of a specified key or value
+ * prevents it from being stored in this map
+ * @throws NullPointerException if a specified key or value is null,
+ * and this map does not permit null keys or values
+ * @throws IllegalArgumentException if some property of a specified key
+ * or value prevents it from being stored in this map
*/
boolean replace(K key, V oldValue, V newValue);
/**
- * Replace entry for key only if currently mapped to some value.
- * Acts as
- * <pre>
- * if ((map.containsKey(key)) {
- * return map.put(key, value);
- * } else return null;
- * </pre>
+ * Replaces the entry for a key only if currently mapped to some value.
+ * This is equivalent to
+ * <pre>
+ * if (map.containsKey(key)) {
+ * return map.put(key, value);
+ * } else return null;</pre>
* except that the action is performed atomically.
- * @param key key with which the specified value is associated.
- * @param value value to be associated with the specified key.
- * @return previous value associated with specified key, or <tt>null</tt>
- * if there was no mapping for key. A <tt>null</tt> return can
- * also indicate that the map previously associated <tt>null</tt>
- * with the specified key, if the implementation supports
- * <tt>null</tt> values.
- * @throws NullPointerException if this map does not permit <tt>null</tt>
- * keys or values, and the specified key or value is
- * <tt>null</tt>.
+ *
+ * @param key key with which the specified value is associated
+ * @param value value to be associated with the specified key
+ * @return the previous value associated with the specified key, or
+ * <tt>null</tt> if there was no mapping for the key.
+ * (A <tt>null</tt> return can also indicate that the map
+ * previously associated <tt>null</tt> with the key,
+ * if the implementation supports null values.)
+ * @throws UnsupportedOperationException if the <tt>put</tt> operation
+ * is not supported by this map
+ * @throws ClassCastException if the class of the specified key or value
+ * prevents it from being stored in this map
+ * @throws NullPointerException if the specified key or value is null,
+ * and this map does not permit null keys or values
+ * @throws IllegalArgumentException if some property of the specified key
+ * or value prevents it from being stored in this map
*/
V replace(K key, V value);
-
}
diff --git a/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java b/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java
index 7274595..f0c8ac6 100644
--- a/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java
+++ b/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java
@@ -1,1190 +1,1317 @@
/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group. Adapted and released, under explicit permission,
+ * from JDK ArrayList.java which carries the following copyright:
+ *
+ * Copyright 1997 by Sun Microsystems, Inc.,
+ * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information
+ * of Sun Microsystems, Inc. ("Confidential Information"). You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with Sun.
*/
package java.util.concurrent;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
+import java.util.*;
+import java.util.concurrent.locks.*;
import java.lang.reflect.Array;
-import java.util.Collection;
-import java.util.ConcurrentModificationException;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.NoSuchElementException;
-import java.util.RandomAccess;
-import java.util.concurrent.locks.ReentrantLock;
-
-// BEGIN android-added
+
+import sun.misc.Unsafe;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
/**
- * Implements a {@link java.util.ArrayList} variant that is thread-safe. All
- * write operation result in a new copy of the underlying data being created.
- * Iterators reflect the state of the CopyOnWriteArrayList at the time they were
- * created. They are not updated to reflect subsequent changes to the list. In
- * addition, these iterators cannot be used for modifying the underlying
- * CopyOnWriteArrayList.
- *
- * @param <E> the element type
+ * A thread-safe variant of {@link java.util.ArrayList} in which all mutative
+ * operations (<tt>add</tt>, <tt>set</tt>, and so on) are implemented by
+ * making a fresh copy of the underlying array.
+ *
+ * <p> This is ordinarily too costly, but may be <em>more</em> efficient
+ * than alternatives when traversal operations vastly outnumber
+ * mutations, and is useful when you cannot or don't want to
+ * synchronize traversals, yet need to preclude interference among
+ * concurrent threads. The "snapshot" style iterator method uses a
+ * reference to the state of the array at the point that the iterator
+ * was created. This array never changes during the lifetime of the
+ * iterator, so interference is impossible and the iterator is
+ * guaranteed not to throw <tt>ConcurrentModificationException</tt>.
+ * The iterator will not reflect additions, removals, or changes to
+ * the list since the iterator was created. Element-changing
+ * operations on iterators themselves (<tt>remove</tt>, <tt>set</tt>, and
+ * <tt>add</tt>) are not supported. These methods throw
+ * <tt>UnsupportedOperationException</tt>.
+ *
+ * <p>All elements are permitted, including <tt>null</tt>.
+ *
+ * <p>Memory consistency effects: As with other concurrent
+ * collections, actions in a thread prior to placing an object into a
+ * {@code CopyOnWriteArrayList}
+ * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
+ * actions subsequent to the access or removal of that element from
+ * the {@code CopyOnWriteArrayList} in another thread.
+ *
+ * @since 1.5
+ * @author Doug Lea
+ * @param <E> the type of elements held in this collection
*/
-// END android-added
-public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
-
+public class CopyOnWriteArrayList<E>
+ implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final long serialVersionUID = 8673264195747942595L;
- private transient volatile E[] arr;
+ /** The lock protecting all mutators */
+ transient final ReentrantLock lock = new ReentrantLock();
- /**
- * Lock for the queue write methods
- */
- private final transient ReentrantLock lock = new ReentrantLock();
+ /** The array, accessed only via getArray/setArray. */
+ private volatile transient Object[] array;
- // BEGIN android-added
/**
- * Creates a new, empty instance of CopyOnWriteArrayList.
+ * Gets the array. Non-private so as to also be accessible
+ * from CopyOnWriteArraySet class.
*/
- // END android-added
- public CopyOnWriteArrayList() {
+ final Object[] getArray() {
+ return array;
}
- // BEGIN android-added
/**
- * Creates a new instance of CopyOnWriteArrayList and fills it with the
- * contents of a given Collection.
- *
- * @param c the collection the elements of which are to be copied into
- * the new instance.
+ * Sets the array.
*/
- // END android-added
- public CopyOnWriteArrayList(Collection<? extends E> c) {
- this((E[]) c.toArray());
+ final void setArray(Object[] a) {
+ array = a;
}
- // BEGIN android-added
/**
- * Creates a new instance of CopyOnWriteArrayList and fills it with the
- * contents of a given array.
- *
- * @param array the array the elements of which are to be copied into the
- * new instance.
+ * Creates an empty list.
*/
- // END android-added
- public CopyOnWriteArrayList(E[] array) {
- int size = array.length;
- E[] data = newElementArray(size);
- for (int i = 0; i < size; i++) {
- data[i] = array[i];
- }
- arr = data;
- }
-
- public boolean add(E e) {
- lock.lock();
- try {
- E[] data;
- E[] old = getData();
- int size = old.length;
- data = newElementArray(size + 1);
- System.arraycopy(old, 0, data, 0, size);
- data[size] = e;
- setData(data);
- return true;
- } finally {
- lock.unlock();
- }
+ public CopyOnWriteArrayList() {
+ setArray(new Object[0]);
}
- public void add(int index, E e) {
- lock.lock();
- try {
- E[] data;
- E[] old = getData();
- int size = old.length;
- checkIndexInclusive(index, size);
- data = newElementArray(size+1);
- System.arraycopy(old, 0, data, 0, index);
- data[index] = e;
- if (size > index) {
- System.arraycopy(old, index, data, index + 1, size - index);
- }
- setData(data);
- } finally {
- lock.unlock();
- }
+ /**
+ * Creates a list containing the elements of the specified
+ * collection, in the order they are returned by the collection's
+ * iterator.
+ *
+ * @param c the collection of initially held elements
+ * @throws NullPointerException if the specified collection is null
+ */
+ public CopyOnWriteArrayList(Collection<? extends E> c) {
+ Object[] elements = c.toArray();
+ // c.toArray might (incorrectly) not return Object[] (see 6260652)
+ if (elements.getClass() != Object[].class)
+ elements = Java6Arrays.copyOf(elements, elements.length, Object[].class);
+ setArray(elements);
}
- public boolean addAll(Collection<? extends E> c) {
- Iterator it = c.iterator();
- int ssize = c.size();
- lock.lock();
- try {
- int size = size();
- E[] data;
- E[] old = getData();
- int nSize = size + ssize;
- data = newElementArray(nSize);
- System.arraycopy(old, 0, data, 0, size);
- while (it.hasNext()) {
- data[size++] = (E) it.next();
- }
- setData(data);
- } finally {
- lock.unlock();
- }
- return true;
+ /**
+ * Creates a list holding a copy of the given array.
+ *
+ * @param toCopyIn the array (a copy of this array is used as the
+ * internal array)
+ * @throws NullPointerException if the specified array is null
+ */
+ public CopyOnWriteArrayList(E[] toCopyIn) {
+ setArray(Java6Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}
- public boolean addAll(int index, Collection<? extends E> c) {
- Iterator it = c.iterator();
- int ssize = c.size();
- lock.lock();
- try {
- int size = size();
- checkIndexInclusive(index, size);
- E[] data;
- E[] old = getData();
- int nSize = size + ssize;
- data = newElementArray(nSize);
- System.arraycopy(old, 0, data, 0, index);
- int i = index;
- while (it.hasNext()) {
- data[i++] = (E) it.next();
- }
- if (size > index) {
- System.arraycopy(old, index, data, index + ssize, size - index);
- }
- setData(data);
- } finally {
- lock.unlock();
- }
- return true;
+ /**
+ * Returns the number of elements in this list.
+ *
+ * @return the number of elements in this list
+ */
+ public int size() {
+ return getArray().length;
}
- // BEGIN android-added
/**
- * Adds to this CopyOnWriteArrayList all those elements from a given
- * collection that are not yet part of the list.
- *
- * @param c the collection from which the potential new elements are
- * taken.
- *
- * @return the number of elements actually added to this list.
+ * Returns <tt>true</tt> if this list contains no elements.
+ *
+ * @return <tt>true</tt> if this list contains no elements
*/
- // END android-added
- public int addAllAbsent(Collection<? extends E> c) {
- if (c.size() == 0) {
- return 0;
- }
- lock.lock();
- try {
- E[] old = getData();
- int size = old.length;
- E[] toAdd = newElementArray(c.size());
- int i = 0;
- for (Iterator it = c.iterator(); it.hasNext();) {
- E o = (E) it.next();
- if (indexOf(o) < 0) {
- toAdd[i++] = o;
- }
- }
- E[] data = newElementArray(size + i);
- System.arraycopy(old, 0, data, 0, size);
- System.arraycopy(toAdd, 0, data, size, i);
- setData(data);
- return i;
- } finally {
- lock.unlock();
- }
+ public boolean isEmpty() {
+ return size() == 0;
}
- // BEGIN android-added
/**
- * Adds to this CopyOnWriteArrayList another element, given that this
- * element is not yet part of the list.
- *
- * @param e the potential new element.
- *
- * @return true if the element was added, or false otherwise.
+ * Test for equality, coping with nulls.
*/
- // END android-added
- public boolean addIfAbsent(E e) {
- lock.lock();
- try {
- E[] data;
- E[] old = getData();
- int size = old.length;
- if (size != 0) {
- if (indexOf(e) >= 0) {
- return false;
- }
- }
- data = newElementArray(size + 1);
- System.arraycopy(old, 0, data, 0, size);
- data[size] = e;
- setData(data);
- return true;
- } finally {
- lock.unlock();
- }
+ private static boolean eq(Object o1, Object o2) {
+ return (o1 == null ? o2 == null : o1.equals(o2));
}
- public void clear() {
- lock.lock();
- try {
- setData(newElementArray(0));
- } finally {
- lock.unlock();
+ /**
+ * static version of indexOf, to allow repeated calls without
+ * needing to re-acquire array each time.
+ * @param o element to search for
+ * @param elements the array
+ * @param index first index to search
+ * @param fence one past last index to search
+ * @return index of element, or -1 if absent
+ */
+ private static int indexOf(Object o, Object[] elements,
+ int index, int fence) {
+ if (o == null) {
+ for (int i = index; i < fence; i++)
+ if (elements[i] == null)
+ return i;
+ } else {
+ for (int i = index; i < fence; i++)
+ if (o.equals(elements[i]))
+ return i;
}
+ return -1;
}
- @Override
- public Object clone() {
- try {
- CopyOnWriteArrayList thisClone = (CopyOnWriteArrayList) super.clone();
- thisClone.setData(this.getData());
- return thisClone;
- } catch (CloneNotSupportedException e) {
- throw new RuntimeException("CloneNotSupportedException is not expected here");
+ /**
+ * static version of lastIndexOf.
+ * @param o element to search for
+ * @param elements the array
+ * @param index first index to search
+ * @return index of element, or -1 if absent
+ */
+ private static int lastIndexOf(Object o, Object[] elements, int index) {
+ if (o == null) {
+ for (int i = index; i >= 0; i--)
+ if (elements[i] == null)
+ return i;
+ } else {
+ for (int i = index; i >= 0; i--)
+ if (o.equals(elements[i]))
+ return i;
}
+ return -1;
}
+ /**
+ * Returns <tt>true</tt> if this list contains the specified element.
+ * More formally, returns <tt>true</tt> if and only if this list contains
+ * at least one element <tt>e</tt> such that
+ * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
+ *
+ * @param o element whose presence in this list is to be tested
+ * @return <tt>true</tt> if this list contains the specified element
+ */
public boolean contains(Object o) {
- return indexOf(o) >= 0;
- }
-
- public boolean containsAll(Collection<?> c) {
- E[] data = getData();
- return containsAll(c, data, 0, data.length);
- }
-
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (!(o instanceof List)) {
- return false;
- }
- List l = (List) o;
- Iterator it = l.listIterator();
- Iterator ourIt = listIterator();
- while (it.hasNext()) {
- if (!ourIt.hasNext()) {
- return false;
- }
- Object thisListElem = it.next();
- Object anotherListElem = ourIt.next();
- if (!(thisListElem == null ? anotherListElem == null : thisListElem
- .equals(anotherListElem))) {
- return false;
- }
- }
- if (ourIt.hasNext()) {
- return false;
- }
- return true;
+ Object[] elements = getArray();
+ return indexOf(o, elements, 0, elements.length) >= 0;
}
- public E get(int index) {
- E[] data = getData();
- return data[index];
- }
-
- public int hashCode() {
- int hashCode = 1;
- Iterator it = listIterator();
- while (it.hasNext()) {
- Object obj = it.next();
- hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
- }
- return hashCode;
+ /**
+ * {@inheritDoc}
+ */
+ public int indexOf(Object o) {
+ Object[] elements = getArray();
+ return indexOf(o, elements, 0, elements.length);
}
- // BEGIN android-added
/**
- * Returns the index of a given element, starting the search from a given
- * position in the list.
- *
- * @param e the element to search.
- * @param index the index at which to start the search.
- *
- * @return the index of the element or null, if the element has not been
- * found at or beyond the given start index.
+ * Returns the index of the first occurrence of the specified element in
+ * this list, searching forwards from <tt>index</tt>, or returns -1 if
+ * the element is not found.
+ * More formally, returns the lowest index <tt>i</tt> such that
+ * <tt>(i&nbsp;&gt;=&nbsp;index&nbsp;&amp;&amp;&nbsp;(e==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;e.equals(get(i))))</tt>,
+ * or -1 if there is no such index.
+ *
+ * @param e element to search for
+ * @param index index to start searching from
+ * @return the index of the first occurrence of the element in
+ * this list at position <tt>index</tt> or later in the list;
+ * <tt>-1</tt> if the element is not found.
+ * @throws IndexOutOfBoundsException if the specified index is negative
*/
- // END android-added
public int indexOf(E e, int index) {
- E[] data = getData();
- return indexOf(e, data, index, data.length - index);
+ Object[] elements = getArray();
+ return indexOf(e, elements, index, elements.length);
}
- public int indexOf(Object o) {
- E[] data = getData();
- return indexOf(o, data, 0, data.length);
+ /**
+ * {@inheritDoc}
+ */
+ public int lastIndexOf(Object o) {
+ Object[] elements = getArray();
+ return lastIndexOf(o, elements, elements.length - 1);
}
- public boolean isEmpty() {
- return size() == 0;
+ /**
+ * Returns the index of the last occurrence of the specified element in
+ * this list, searching backwards from <tt>index</tt>, or returns -1 if
+ * the element is not found.
+ * More formally, returns the highest index <tt>i</tt> such that
+ * <tt>(i&nbsp;&lt;=&nbsp;index&nbsp;&amp;&amp;&nbsp;(e==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;e.equals(get(i))))</tt>,
+ * or -1 if there is no such index.
+ *
+ * @param e element to search for
+ * @param index index to start searching backwards from
+ * @return the index of the last occurrence of the element at position
+ * less than or equal to <tt>index</tt> in this list;
+ * -1 if the element is not found.
+ * @throws IndexOutOfBoundsException if the specified index is greater
+ * than or equal to the current size of this list
+ */
+ public int lastIndexOf(E e, int index) {
+ Object[] elements = getArray();
+ return lastIndexOf(e, elements, index);
}
- public Iterator<E> iterator() {
- return new ListIteratorImpl(getData(), 0);
+ /**
+ * Returns a shallow copy of this list. (The elements themselves
+ * are not copied.)
+ *
+ * @return a clone of this list
+ */
+ public Object clone() {
+ try {
+ CopyOnWriteArrayList c = (CopyOnWriteArrayList)(super.clone());
+ c.resetLock();
+ return c;
+ } catch (CloneNotSupportedException e) {
+ // this shouldn't happen, since we are Cloneable
+ throw new InternalError();
+ }
}
- // BEGIN android-added
/**
- * Returns the last index of a given element, starting the search from
- * a given position in the list and going backwards.
- *
- * @param e the element to search.
- * @param index the index at which to start the search.
- *
- * @return the index of the element or null, if the element has not been
- * found at or before the given start index.
+ * Returns an array containing all of the elements in this list
+ * in proper sequence (from first to last element).
+ *
+ * <p>The returned array will be "safe" in that no references to it are
+ * maintained by this list. (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 the elements in this list
*/
- // END android-added
- public int lastIndexOf(E e, int index) {
- E[] data = getData();
- return lastIndexOf(e, data, 0, index);
+ public Object[] toArray() {
+ Object[] elements = getArray();
+ return Java6Arrays.copyOf(elements, elements.length);
}
- public int lastIndexOf(Object o) {
- E[] data = getData();
- return lastIndexOf(o, data, 0, data.length);
+ /**
+ * Returns an array containing all of the elements in this list in
+ * proper sequence (from first to last element); the runtime type of
+ * the returned array is that of the specified array. If the list 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 list.
+ *
+ * <p>If this list fits in the specified array with room to spare
+ * (i.e., the array has more elements than this list), the element in
+ * the array immediately following the end of the list is set to
+ * <tt>null</tt>. (This is useful in determining the length of this
+ * list <i>only</i> if the caller knows that this list does not contain
+ * any null elements.)
+ *
+ * <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 list known to contain only strings.
+ * The following code can be used to dump the list 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 list 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 the elements in this list
+ * @throws ArrayStoreException if the runtime type of the specified array
+ * is not a supertype of the runtime type of every element in
+ * this list
+ * @throws NullPointerException if the specified array is null
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T[] toArray(T a[]) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (a.length < len)
+ return (T[]) Java6Arrays.copyOf(elements, len, a.getClass());
+ else {
+ System.arraycopy(elements, 0, a, 0, len);
+ if (a.length > len)
+ a[len] = null;
+ return a;
+ }
}
- public ListIterator<E> listIterator() {
- return new ListIteratorImpl(getData(), 0);
- }
+ // Positional Access Operations
- public ListIterator<E> listIterator(int index) {
- E[] data = getData();
- checkIndexInclusive(index, data.length);
- return new ListIteratorImpl(data, index);
+ @SuppressWarnings("unchecked")
+ private E get(Object[] a, int index) {
+ return (E) a[index];
}
- public E remove(int index) {
- return removeRange(index, 1);
+ /**
+ * {@inheritDoc}
+ *
+ * @throws IndexOutOfBoundsException {@inheritDoc}
+ */
+ public E get(int index) {
+ return get(getArray(), index);
}
- public boolean remove(Object o) {
+ /**
+ * Replaces the element at the specified position in this list with the
+ * specified element.
+ *
+ * @throws IndexOutOfBoundsException {@inheritDoc}
+ */
+ public E set(int index, E element) {
+ final ReentrantLock lock = this.lock;
lock.lock();
try {
- int index = indexOf(o);
- if (index == -1) {
- return false;
+ Object[] elements = getArray();
+ E oldValue = get(elements, index);
+
+ if (oldValue != element) {
+ int len = elements.length;
+ Object[] newElements = Java6Arrays.copyOf(elements, len);
+ newElements[index] = element;
+ setArray(newElements);
+ } else {
+ // Not quite a no-op; ensures volatile write semantics
+ setArray(elements);
}
- remove(index);
- return true;
+ return oldValue;
} finally {
lock.unlock();
}
}
- public boolean removeAll(Collection<?> c) {
+ /**
+ * Appends the specified element to the end of this list.
+ *
+ * @param e element to be appended to this list
+ * @return <tt>true</tt> (as specified by {@link Collection#add})
+ */
+ public boolean add(E e) {
+ final ReentrantLock lock = this.lock;
lock.lock();
try {
- return removeAll(c, 0, getData().length) != 0;
+ Object[] elements = getArray();
+ int len = elements.length;
+ Object[] newElements = Java6Arrays.copyOf(elements, len + 1);
+ newElements[len] = e;
+ setArray(newElements);
+ return true;
} finally {
lock.unlock();
}
}
- public boolean retainAll(Collection<?> c) {
- if (c == null) {
- throw new NullPointerException();
- }
+ /**
+ * Inserts the specified element at the specified position in this
+ * list. Shifts the element currently at that position (if any) and
+ * any subsequent elements to the right (adds one to their indices).
+ *
+ * @throws IndexOutOfBoundsException {@inheritDoc}
+ */
+ public void add(int index, E element) {
+ final ReentrantLock lock = this.lock;
lock.lock();
try {
- return retainAll(c, 0, getData().length) != 0;
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (index > len || index < 0)
+ throw new IndexOutOfBoundsException("Index: "+index+
+ ", Size: "+len);
+ Object[] newElements;
+ int numMoved = len - index;
+ if (numMoved == 0)
+ newElements = Java6Arrays.copyOf(elements, len + 1);
+ else {
+ newElements = new Object[len + 1];
+ System.arraycopy(elements, 0, newElements, 0, index);
+ System.arraycopy(elements, index, newElements, index + 1,
+ numMoved);
+ }
+ newElements[index] = element;
+ setArray(newElements);
} finally {
lock.unlock();
}
}
- public E set(int index, E e) {
+ /**
+ * Removes the element at the specified position in this list.
+ * Shifts any subsequent elements to the left (subtracts one from their
+ * indices). Returns the element that was removed from the list.
+ *
+ * @throws IndexOutOfBoundsException {@inheritDoc}
+ */
+ public E remove(int index) {
+ final ReentrantLock lock = this.lock;
lock.lock();
try {
- int size = size();
- checkIndexExlusive(index, size);
- E[] data;
- data = newElementArray(size);
- E[] oldArr = getData();
- System.arraycopy(oldArr, 0, data, 0, size);
- E old = data[index];
- data[index] = e;
- setData(data);
- return old;
+ Object[] elements = getArray();
+ int len = elements.length;
+ E oldValue = get(elements, index);
+ int numMoved = len - index - 1;
+ if (numMoved == 0)
+ setArray(Java6Arrays.copyOf(elements, len - 1));
+ else {
+ Object[] newElements = new Object[len - 1];
+ System.arraycopy(elements, 0, newElements, 0, index);
+ System.arraycopy(elements, index + 1, newElements, index,
+ numMoved);
+ setArray(newElements);
+ }
+ return oldValue;
} finally {
lock.unlock();
}
}
- public int size() {
- return getData().length;
- }
-
- public List<E> subList(int fromIndex, int toIndex) {
- return new SubList(this, fromIndex, toIndex);
- }
-
- public Object[] toArray() {
- E[] data = getData();
- return toArray(data, 0, data.length);
- }
-
- public <T> T[] toArray(T[] a) {
- E[] data = getData();
- return (T[]) toArray(a, data, 0, data.length);
- }
-
- @Override
- public String toString() {
- StringBuffer sb = new StringBuffer("[");
+ /**
+ * Removes the first occurrence of the specified element from this list,
+ * if it is present. If this list does not contain the element, it is
+ * unchanged. More formally, removes the element with the lowest index
+ * <tt>i</tt> such that
+ * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
+ * (if such an element exists). Returns <tt>true</tt> if this list
+ * contained the specified element (or equivalently, if this list
+ * changed as a result of the call).
+ *
+ * @param o element to be removed from this list, if present
+ * @return <tt>true</tt> if this list contained the specified element
+ */
+ public boolean remove(Object o) {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (len != 0) {
+ // Copy while searching for element to remove
+ // This wins in the normal case of element being present
+ int newlen = len - 1;
+ Object[] newElements = new Object[newlen];
+
+ for (int i = 0; i < newlen; ++i) {
+ if (eq(o, elements[i])) {
+ // found one; copy remaining and exit
+ for (int k = i + 1; k < len; ++k)
+ newElements[k-1] = elements[k];
+ setArray(newElements);
+ return true;
+ } else
+ newElements[i] = elements[i];
+ }
- Iterator it = listIterator();
- while (it.hasNext()) {
- sb.append(String.valueOf(it.next()));
- sb.append(", ");
- }
- if (sb.length() > 1) {
- sb.setLength(sb.length() - 2);
+ // special handling for last cell
+ if (eq(o, elements[newlen])) {
+ setArray(newElements);
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ lock.unlock();
}
- sb.append("]");
- return sb.toString();
}
- // private and package private methods
-
- @SuppressWarnings("unchecked")
- private final E[] newElementArray(int size) {
- return (E[])new Object[size];
+ /**
+ * Removes from this list all of the elements whose index is between
+ * <tt>fromIndex</tt>, inclusive, and <tt>toIndex</tt>, exclusive.
+ * Shifts any succeeding elements to the left (reduces their index).
+ * This call shortens the list by <tt>(toIndex - fromIndex)</tt> elements.
+ * (If <tt>toIndex==fromIndex</tt>, this operation has no effect.)
+ *
+ * @param fromIndex index of first element to be removed
+ * @param toIndex index after last element to be removed
+ * @throws IndexOutOfBoundsException if fromIndex or toIndex out of range
+ * (@code{fromIndex < 0 || toIndex > size() || toIndex < fromIndex})
+ */
+ private void removeRange(int fromIndex, int toIndex) {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ Object[] elements = getArray();
+ int len = elements.length;
+
+ if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
+ throw new IndexOutOfBoundsException();
+ int newlen = len - (toIndex - fromIndex);
+ int numMoved = len - toIndex;
+ if (numMoved == 0)
+ setArray(Java6Arrays.copyOf(elements, newlen));
+ else {
+ Object[] newElements = new Object[newlen];
+ System.arraycopy(elements, 0, newElements, 0, fromIndex);
+ System.arraycopy(elements, toIndex, newElements,
+ fromIndex, numMoved);
+ setArray(newElements);
+ }
+ } finally {
+ lock.unlock();
+ }
}
/**
- * sets the internal data array
+ * Append the element if not present.
*
- * @param data array to set
+ * @param e element to be added to this list, if absent
+ * @return <tt>true</tt> if the element was added
*/
- private final void setData(E[] data) {
- arr = data;
+ public boolean addIfAbsent(E e) {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ // Copy while checking if already present.
+ // This wins in the most common case where it is not present
+ Object[] elements = getArray();
+ int len = elements.length;
+ Object[] newElements = new Object[len + 1];
+ for (int i = 0; i < len; ++i) {
+ if (eq(e, elements[i]))
+ return false; // exit, throwing away copy
+ else
+ newElements[i] = elements[i];
+ }
+ newElements[len] = e;
+ setArray(newElements);
+ return true;
+ } finally {
+ lock.unlock();
+ }
}
/**
- * gets the internal data array
+ * Returns <tt>true</tt> if this list contains all of the elements of the
+ * specified collection.
*
- * @return the data array
+ * @param c collection to be checked for containment in this list
+ * @return <tt>true</tt> if this list contains all of the elements of the
+ * specified collection
+ * @throws NullPointerException if the specified collection is null
+ * @see #contains(Object)
*/
- final E[] getData() {
- if (arr == null) {
- return newElementArray(0);
+ public boolean containsAll(Collection<?> c) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ for (Object e : c) {
+ if (indexOf(e, elements, 0, len) < 0)
+ return false;
}
- return arr;
+ return true;
}
/**
- * Removes from the specified range of this list
- * all the elements that are contained in the specified collection
- * <p/>
- * !should be called under lock
+ * Removes from this list all of its elements that are contained in
+ * the specified collection. This is a particularly expensive operation
+ * in this class because of the need for an internal temporary array.
*
- * @return Returns the number of removed elements
+ * @param c collection containing elements to be removed from this list
+ * @return <tt>true</tt> if this list changed as a result of the call
+ * @throws ClassCastException if the class of an element of this list
+ * is incompatible with the specified collection (optional)
+ * @throws NullPointerException if this list contains a null element and the
+ * specified collection does not permit null elements (optional),
+ * or if the specified collection is null
+ * @see #remove(Object)
*/
- final int removeAll(Collection c, int start, int size) {
- int ssize = c.size();
- if (ssize == 0) {
- return 0;
- }
- Object[] old = getData();
- int arrsize = old.length;
- if (arrsize == 0) {
- return 0;
- }
- Object[] data = new Object[size];
- int j = 0;
- for (int i = start; i < (start + size); i++) {
- if (!c.contains(old[i])) {
- data[j++] = old[i];
+ public boolean removeAll(Collection<?> c) {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (len != 0) {
+ // temp array holds those elements we know we want to keep
+ int newlen = 0;
+ Object[] temp = new Object[len];
+ for (int i = 0; i < len; ++i) {
+ Object element = elements[i];
+ if (!c.contains(element))
+ temp[newlen++] = element;
+ }
+ if (newlen != len) {
+ setArray(Java6Arrays.copyOf(temp, newlen));
+ return true;
+ }
}
+ return false;
+ } finally {
+ lock.unlock();
}
- if (j != size) {
- E[] result = newElementArray(arrsize - (size - j));
- System.arraycopy(old, 0, result, 0, start);
- System.arraycopy(data, 0, result, start, j);
- System.arraycopy(old, start + size, result, start + j, arrsize
- - (start + size));
- setData(result);
- return (size - j);
- }
- return 0;
}
/**
- * Retains only the elements in the specified range of this list
- * that are contained in the specified collection
+ * Retains only the elements in this list that are contained in the
+ * specified collection. In other words, removes from this list all of
+ * its elements that are not contained in the specified collection.
*
- * @return Returns the number of removed elements
+ * @param c collection containing elements to be retained in this list
+ * @return <tt>true</tt> if this list changed as a result of the call
+ * @throws ClassCastException if the class of an element of this list
+ * is incompatible with the specified collection (optional)
+ * @throws NullPointerException if this list contains a null element and the
+ * specified collection does not permit null elements (optional),
+ * or if the specified collection is null
+ * @see #remove(Object)
*/
- // should be called under lock
- int retainAll(Collection c, int start, int size) {
- Object[] old = getData();
- if (size == 0) {
- return 0;
- }
- if (c.size() == 0) {
- E[] data;
- if (size == old.length) {
- data = newElementArray(0);
- } else {
- data = newElementArray(old.length - size);
- System.arraycopy(old, 0, data, 0, start);
- System.arraycopy(old, start + size, data, start, old.length
- - start - size);
- }
- setData(data);
- return size;
- }
- Object[] temp = new Object[size];
- int pos = 0;
- for (int i = start; i < (start + size); i++) {
- if (c.contains(old[i])) {
- temp[pos++] = old[i];
+ public boolean retainAll(Collection<?> c) {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (len != 0) {
+ // temp array holds those elements we know we want to keep
+ int newlen = 0;
+ Object[] temp = new Object[len];
+ for (int i = 0; i < len; ++i) {
+ Object element = elements[i];
+ if (c.contains(element))
+ temp[newlen++] = element;
+ }
+ if (newlen != len) {
+ setArray(Java6Arrays.copyOf(temp, newlen));
+ return true;
+ }
}
+ return false;
+ } finally {
+ lock.unlock();
}
- if (pos == size) {
- return 0;
- }
- E[] data = newElementArray(pos + old.length - size);
- System.arraycopy(old, 0, data, 0, start);
- System.arraycopy(temp, 0, data, start, pos);
- System.arraycopy(old, start + size, data, start + pos, old.length
- - start - size);
- setData(data);
- return (size - pos);
}
/**
- * Removes specified range from this list
+ * Appends all of the elements in the specified collection that
+ * are not already contained in this list, to the end of
+ * this list, in the order that they are returned by the
+ * specified collection's iterator.
+ *
+ * @param c collection containing elements to be added to this list
+ * @return the number of elements added
+ * @throws NullPointerException if the specified collection is null
+ * @see #addIfAbsent(Object)
*/
- E removeRange(int start, int size) {
+ public int addAllAbsent(Collection<? extends E> c) {
+ Object[] cs = c.toArray();
+ if (cs.length == 0)
+ return 0;
+ Object[] uniq = new Object[cs.length];
+ final ReentrantLock lock = this.lock;
lock.lock();
try {
- int sizeArr = size();
- checkIndexExlusive(start, sizeArr);
- checkIndexInclusive(start + size, sizeArr);
- E[] data;
- data = newElementArray(sizeArr - size);
- E[] oldArr = getData();
- System.arraycopy(oldArr, 0, data, 0, start);
- E old = oldArr[start];
- if (sizeArr > (start + size)) {
- System.arraycopy(oldArr, start + size, data, start, sizeArr
- - (start + size));
+ Object[] elements = getArray();
+ int len = elements.length;
+ int added = 0;
+ for (int i = 0; i < cs.length; ++i) { // scan for duplicates
+ Object e = cs[i];
+ if (indexOf(e, elements, 0, len) < 0 &&
+ indexOf(e, uniq, 0, added) < 0)
+ uniq[added++] = e;
}
- setData(data);
- return old;
+ if (added > 0) {
+ Object[] newElements = Java6Arrays.copyOf(elements, len + added);
+ System.arraycopy(uniq, 0, newElements, len, added);
+ setArray(newElements);
+ }
+ return added;
} finally {
lock.unlock();
}
}
- // some util static functions to use by iterators and methods
/**
- * Returns an array containing all of the elements
- * in the specified range of the array in proper sequence
+ * Removes all of the elements from this list.
+ * The list will be empty after this call returns.
*/
- static Object[] toArray(Object[] data, int start, int size) {
- Object[] result = new Object[size];
- System.arraycopy(data, start, result, 0, size);
- return result;
+ public void clear() {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ setArray(new Object[0]);
+ } finally {
+ lock.unlock();
+ }
}
/**
- * Returns an array containing all of the elements
- * in the specified range of the array in proper sequence,
- * stores the result in the array, specified by first parameter
- * (as for public instance method toArray(Object[] to)
+ * Appends all of the elements in the specified collection to the end
+ * of this list, in the order that they are returned by the specified
+ * collection's iterator.
+ *
+ * @param c collection containing elements to be added to this list
+ * @return <tt>true</tt> if this list changed as a result of the call
+ * @throws NullPointerException if the specified collection is null
+ * @see #add(Object)
*/
- static Object[] toArray(Object[] to, Object[] data, int start, int size) {
- int l = data.length;
- if (to.length < l) {
- to = (Object[]) Array.newInstance(to.getClass().getComponentType(),
- l);
- } else {
- if (to.length > l) {
- to[l] = null;
- }
+ public boolean addAll(Collection<? extends E> c) {
+ Object[] cs = c.toArray();
+ if (cs.length == 0)
+ return false;
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ Object[] elements = getArray();
+ int len = elements.length;
+ Object[] newElements = Java6Arrays.copyOf(elements, len + cs.length);
+ System.arraycopy(cs, 0, newElements, len, cs.length);
+ setArray(newElements);
+ return true;
+ } finally {
+ lock.unlock();
}
- System.arraycopy(data, start, to, 0, size);
- return to;
}
/**
- * Checks if the specified range of the
- * array contains all of the elements in the collection
+ * Inserts all of the elements in the specified collection into this
+ * list, starting at the specified position. Shifts the element
+ * currently at that position (if any) and any subsequent elements to
+ * the right (increases their indices). The new elements will appear
+ * in this list in the order that they are returned by the
+ * specified collection's iterator.
*
- * @param c collection with elements
- * @param data array where to search the elements
- * @param start start index
- * @param size size of the range
+ * @param index index at which to insert the first element
+ * from the specified collection
+ * @param c collection containing elements to be added to this list
+ * @return <tt>true</tt> if this list changed as a result of the call
+ * @throws IndexOutOfBoundsException {@inheritDoc}
+ * @throws NullPointerException if the specified collection is null
+ * @see #add(int,Object)
*/
- static final boolean containsAll(Collection c, Object[] data, int start,
- int size) {
- if (size == 0) {
- return false;
- }
- Iterator it = c.iterator();
- while (it.hasNext()) {
- Object next = it.next();
- if (indexOf(next, data, start, size) < 0) {
+ public boolean addAll(int index, Collection<? extends E> c) {
+ Object[] cs = c.toArray();
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (index > len || index < 0)
+ throw new IndexOutOfBoundsException("Index: "+index+
+ ", Size: "+len);
+ if (cs.length == 0)
return false;
+ int numMoved = len - index;
+ Object[] newElements;
+ if (numMoved == 0)
+ newElements = Java6Arrays.copyOf(elements, len + cs.length);
+ else {
+ newElements = new Object[len + cs.length];
+ System.arraycopy(elements, 0, newElements, 0, index);
+ System.arraycopy(elements, index,
+ newElements, index + cs.length,
+ numMoved);
}
+ System.arraycopy(cs, 0, newElements, index, cs.length);
+ setArray(newElements);
+ return true;
+ } finally {
+ lock.unlock();
}
- return true;
}
/**
- * Returns the index in the specified range of the data array
- * of the last occurrence of the specified element
+ * Save the state of the list to a stream (i.e., serialize it).
*
- * @param o element to search
- * @param data array where to search
- * @param start start index
- * @param size size of the range
- * @return
+ * @serialData The length of the array backing the list is emitted
+ * (int), followed by all of its elements (each an Object)
+ * in the proper order.
+ * @param s the stream
*/
- static final int lastIndexOf(Object o, Object[] data, int start, int size) {
- if (size == 0) {
- return -1;
- }
- if (o != null) {
- for (int i = start + size - 1; i > start - 1; i--) {
- if (o.equals(data[i])) {
- return i;
- }
- }
- } else {
- for (int i = start + size - 1; i > start - 1; i--) {
- if (data[i] == null) {
- return i;
- }
- }
- }
- return -1;
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException{
+
+ // Write out element count, and any hidden stuff
+ s.defaultWriteObject();
+
+ Object[] elements = getArray();
+ int len = elements.length;
+ // Write out array length
+ s.writeInt(len);
+
+ // Write out all elements in the proper order.
+ for (int i = 0; i < len; i++)
+ s.writeObject(elements[i]);
}
/**
- * Returns the index in the specified range of the data array
- * of the first occurrence of the specified element
+ * Reconstitute the list from a stream (i.e., deserialize it).
+ * @param s the stream
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+
+ // Read in size, and any hidden stuff
+ s.defaultReadObject();
+
+ // bind to new lock
+ resetLock();
+
+ // Read in array length and allocate array
+ int len = s.readInt();
+ Object[] elements = new Object[len];
+
+ // Read in all elements in the proper order.
+ for (int i = 0; i < len; i++)
+ elements[i] = s.readObject();
+ setArray(elements);
+ }
+
+ /**
+ * Returns a string representation of this list. The string
+ * representation consists of the string representations of the list's
+ * elements in the order they are returned by its iterator, enclosed in
+ * square brackets (<tt>"[]"</tt>). Adjacent elements are separated by
+ * the characters <tt>", "</tt> (comma and space). Elements are
+ * converted to strings as by {@link String#valueOf(Object)}.
*
- * @param o element to search
- * @param data array where to search
- * @param start start index
- * @param size end index
- * @return
+ * @return a string representation of this list
*/
- static final int indexOf(Object o, Object[] data, int start, int size) {
- if (size == 0) {
- return -1;
- }
- if (o == null) {
- for (int i = start; i < start + size; i++) {
- if (data[i] == null) {
- return i;
- }
- }
- } else {
- for (int i = start; i < start + size; i++) {
- if (o.equals(data[i])) {
- return i;
- }
- }
- }
- return -1;
+ public String toString() {
+ return Arrays.toString(getArray());
}
/**
- * Throws <code>IndexOutOfBoundsException</code> if <code>index</code>
- * is out of the list bounds.
+ * Compares the specified object with this list for equality.
+ * Returns {@code true} if the specified object is the same object
+ * as this object, or if it is also a {@link List} and the sequence
+ * of elements returned by an {@linkplain List#iterator() iterator}
+ * over the specified list is the same as the sequence returned by
+ * an iterator over this list. The two sequences are considered to
+ * be the same if they have the same length and corresponding
+ * elements at the same position in the sequence are <em>equal</em>.
+ * Two elements {@code e1} and {@code e2} are considered
+ * <em>equal</em> if {@code (e1==null ? e2==null : e1.equals(e2))}.
*
- * @param index element index to check.
+ * @param o the object to be compared for equality with this list
+ * @return {@code true} if the specified object is equal to this list
*/
- static final void checkIndexInclusive(int index, int size) {
- if (index < 0 || index > size) {
- throw new IndexOutOfBoundsException("Index is " + index + ", size is " + size);
- }
+ public boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (!(o instanceof List))
+ return false;
+
+ List<?> list = (List<?>)(o);
+ Iterator<?> it = list.iterator();
+ Object[] elements = getArray();
+ int len = elements.length;
+ for (int i = 0; i < len; ++i)
+ if (!it.hasNext() || !eq(elements[i], it.next()))
+ return false;
+ if (it.hasNext())
+ return false;
+ return true;
}
/**
- * Throws <code>IndexOutOfBoundsException</code> if <code>index</code>
- * is out of the list bounds. Excluding the last element.
+ * Returns the hash code value for this list.
+ *
+ * <p>This implementation uses the definition in {@link List#hashCode}.
*
- * @param index element index to check.
+ * @return the hash code value for this list
*/
- static final void checkIndexExlusive(int index, int size) {
- if (index < 0 || index >= size) {
- throw new IndexOutOfBoundsException("Index is " + index + ", size is " + size);
+ public int hashCode() {
+ int hashCode = 1;
+ Object[] elements = getArray();
+ int len = elements.length;
+ for (int i = 0; i < len; ++i) {
+ Object obj = elements[i];
+ hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
}
+ return hashCode;
}
/**
- * list iterator implementation,
- * when created gets snapshot of the list,
- * so never throws ConcurrentModificationException
+ * Returns an iterator over the elements in this list in proper sequence.
+ *
+ * <p>The returned iterator provides a snapshot of the state of the list
+ * when the iterator was constructed. No synchronization is needed while
+ * traversing the iterator. The iterator does <em>NOT</em> support the
+ * <tt>remove</tt> method.
+ *
+ * @return an iterator over the elements in this list in proper sequence
*/
- private static class ListIteratorImpl implements ListIterator {
- private final Object[] arr;
+ public Iterator<E> iterator() {
+ return new COWIterator<E>(getArray(), 0);
+ }
- private int current;
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The returned iterator provides a snapshot of the state of the list
+ * when the iterator was constructed. No synchronization is needed while
+ * traversing the iterator. The iterator does <em>NOT</em> support the
+ * <tt>remove</tt>, <tt>set</tt> or <tt>add</tt> methods.
+ */
+ public ListIterator<E> listIterator() {
+ return new COWIterator<E>(getArray(), 0);
+ }
- private final int size;
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The returned iterator provides a snapshot of the state of the list
+ * when the iterator was constructed. No synchronization is needed while
+ * traversing the iterator. The iterator does <em>NOT</em> support the
+ * <tt>remove</tt>, <tt>set</tt> or <tt>add</tt> methods.
+ *
+ * @throws IndexOutOfBoundsException {@inheritDoc}
+ */
+ public ListIterator<E> listIterator(final int index) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (index<0 || index>len)
+ throw new IndexOutOfBoundsException("Index: "+index);
- final int size() {
- return size;
- }
+ return new COWIterator<E>(elements, index);
+ }
- public ListIteratorImpl(Object[] data, int current) {
- this.current = current;
- arr = data;
- size = data.length;
- }
+ private static class COWIterator<E> implements ListIterator<E> {
+ /** Snapshot of the array **/
+ private final Object[] snapshot;
+ /** Index of element to be returned by subsequent call to next. */
+ private int cursor;
- public void add(Object o) {
- throw new UnsupportedOperationException("Unsupported operation add");
+ private COWIterator(Object[] elements, int initialCursor) {
+ cursor = initialCursor;
+ snapshot = elements;
}
public boolean hasNext() {
- if (current < size) {
- return true;
- }
- return false;
+ return cursor < snapshot.length;
}
public boolean hasPrevious() {
- return current > 0;
+ return cursor > 0;
}
- public Object next() {
- if (hasNext()) {
- return arr[current++];
- }
- throw new NoSuchElementException("pos is " + current + ", size is " + size);
+ @SuppressWarnings("unchecked")
+ public E next() {
+ if (! hasNext())
+ throw new NoSuchElementException();
+ return (E) snapshot[cursor++];
}
- public int nextIndex() {
- return current;
+ @SuppressWarnings("unchecked")
+ public E previous() {
+ if (! hasPrevious())
+ throw new NoSuchElementException();
+ return (E) snapshot[--cursor];
}
- public Object previous() {
- if (hasPrevious()) {
- return arr[--current];
- }
- throw new NoSuchElementException("pos is " + (current-1) + ", size is " + size);
+ public int nextIndex() {
+ return cursor;
}
public int previousIndex() {
- return current - 1;
+ return cursor-1;
}
+ /**
+ * Not supported. Always throws UnsupportedOperationException.
+ * @throws UnsupportedOperationException always; <tt>remove</tt>
+ * is not supported by this iterator.
+ */
public void remove() {
- throw new UnsupportedOperationException("Unsupported operation remove");
+ throw new UnsupportedOperationException();
}
- public void set(Object o) {
- throw new UnsupportedOperationException("Unsupported operation set");
+ /**
+ * Not supported. Always throws UnsupportedOperationException.
+ * @throws UnsupportedOperationException always; <tt>set</tt>
+ * is not supported by this iterator.
+ */
+ public void set(E e) {
+ throw new UnsupportedOperationException();
}
+ /**
+ * Not supported. Always throws UnsupportedOperationException.
+ * @throws UnsupportedOperationException always; <tt>add</tt>
+ * is not supported by this iterator.
+ */
+ public void add(E e) {
+ throw new UnsupportedOperationException();
+ }
}
/**
- * Keeps a state of sublist implementation,
- * size and array declared as final,
- * so we'll never get the unconsistent state
+ * Returns a view of the portion of this list between
+ * <tt>fromIndex</tt>, inclusive, and <tt>toIndex</tt>, exclusive.
+ * The returned list is backed by this list, so changes in the
+ * returned list are reflected in this list.
+ *
+ * <p>The semantics of the list returned by this method become
+ * undefined if the backing list (i.e., this list) is modified in
+ * any way other than via the returned list.
+ *
+ * @param fromIndex low endpoint (inclusive) of the subList
+ * @param toIndex high endpoint (exclusive) of the subList
+ * @return a view of the specified range within this list
+ * @throws IndexOutOfBoundsException {@inheritDoc}
*/
- static final class SubListReadData {
- final int size;
-
- final Object[] data;
-
- SubListReadData(int size, Object[] data) {
- this.size = size;
- this.data = data;
+ public List<E> subList(int fromIndex, int toIndex) {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (fromIndex < 0 || toIndex > len || fromIndex > toIndex)
+ throw new IndexOutOfBoundsException();
+ return new COWSubList<E>(this, fromIndex, toIndex);
+ } finally {
+ lock.unlock();
}
}
/**
- * Represents a list returned by <code>sublist()</code>.
+ * Sublist for CopyOnWriteArrayList.
+ * This class extends AbstractList merely for convenience, to
+ * avoid having to define addAll, etc. This doesn't hurt, but
+ * is wasteful. This class does not need or use modCount
+ * mechanics in AbstractList, but does need to check for
+ * concurrent modification using similar mechanics. On each
+ * operation, the array that we expect the backing list to use
+ * is checked and updated. Since we do this for all of the
+ * base operations invoked by those defined in AbstractList,
+ * all is well. While inefficient, this is not worth
+ * improving. The kinds of list operations inherited from
+ * AbstractList are already so slow on COW sublists that
+ * adding a bit more space/time doesn't seem even noticeable.
*/
- static class SubList implements List {
- private final CopyOnWriteArrayList list;
-
- private volatile SubListReadData read;
-
- private final int start;
-
- /**
- * Sublist constructor.
- *
- * @param list backing list.
- * @param fromIdx startingIndex, inclusive
- * @param toIdx endIndex, exclusive
- */
- public SubList(CopyOnWriteArrayList list, int fromIdx, int toIdx) {
- this.list = list;
- Object[] data = list.getData();
- int size = toIdx - fromIdx;
- checkIndexExlusive(fromIdx, data.length);
- checkIndexInclusive(toIdx, data.length);
- read = new SubListReadData(size, list.getData());
- start = fromIdx;
- }
-
- /**
- * throws ConcurrentModificationException when the list
- * is structurally modified in the other way other than via this subList
- * <p/>
- * Should be called under lock!
- */
- private void checkModifications() {
- if (read.data != list.getData()) {
+ private static class COWSubList<E>
+ extends AbstractList<E>
+ implements RandomAccess
+ {
+ private final CopyOnWriteArrayList<E> l;
+ private final int offset;
+ private int size;
+ private Object[] expectedArray;
+
+ // only call this holding l's lock
+ COWSubList(CopyOnWriteArrayList<E> list,
+ int fromIndex, int toIndex) {
+ l = list;
+ expectedArray = l.getArray();
+ offset = fromIndex;
+ size = toIndex - fromIndex;
+ }
+
+ // only call this holding l's lock
+ private void checkForComodification() {
+ if (l.getArray() != expectedArray)
throw new ConcurrentModificationException();
- }
}
- /**
- * @see java.util.List#listIterator(int)
- */
- public ListIterator listIterator(int startIdx) {
- return new SubListIterator(startIdx, read);
+ // only call this holding l's lock
+ private void rangeCheck(int index) {
+ if (index<0 || index>=size)
+ throw new IndexOutOfBoundsException("Index: "+index+
+ ",Size: "+size);
}
- /**
- * @see java.util.List#set(int, java.lang.Object)
- */
- public Object set(int index, Object obj) {
- list.lock.lock();
+ public E set(int index, E element) {
+ final ReentrantLock lock = l.lock;
+ lock.lock();
try {
- checkIndexExlusive(index, read.size);
- checkModifications();
- Object result = list.set(index + start, obj);
- read = new SubListReadData(read.size, list.getData());
- return result;
+ rangeCheck(index);
+ checkForComodification();
+ E x = l.set(index+offset, element);
+ expectedArray = l.getArray();
+ return x;
} finally {
- list.lock.unlock();
+ lock.unlock();
}
}
- /**
- * @see java.util.List#get(int)
- */
- public Object get(int index) {
- SubListReadData data = read;
- if (data.data != list.getData()) {
- list.lock.lock();
- try {
- data = read;
- if (data.data != list.getData()) {
- throw new ConcurrentModificationException();
- }
- } finally {
- list.lock.unlock();
- }
- }
- checkIndexExlusive(index, data.size);
- return data.data[index + start];
- }
-
- /**
- * @see java.util.Collection#size()
- */
- public int size() {
- return read.size;
- }
-
- /**
- * @see java.util.List#remove(int)
- */
- public Object remove(int index) {
- list.lock.lock();
+ public E get(int index) {
+ final ReentrantLock lock = l.lock;
+ lock.lock();
try {
- checkIndexExlusive(index, read.size);
- checkModifications();
- Object obj = list.remove(index + start);
- read = new SubListReadData(read.size - 1, list.getData());
- return obj;
+ rangeCheck(index);
+ checkForComodification();
+ return l.get(index+offset);
} finally {
- list.lock.unlock();
+ lock.unlock();
}
}
- /**
- * @see java.util.List#add(int, java.lang.Object)
- */
- public void add(int index, Object object) {
- list.lock.lock();
+ public int size() {
+ final ReentrantLock lock = l.lock;
+ lock.lock();
try {
- checkIndexInclusive(index, read.size);
- checkModifications();
- list.add(index + start, object);
- read = new SubListReadData(read.size + 1, list.getData());
+ checkForComodification();
+ return size;
} finally {
- list.lock.unlock();
+ lock.unlock();
}
}
- public boolean add(Object o) {
- list.lock.lock();
+ public void add(int index, E element) {
+ final ReentrantLock lock = l.lock;
+ lock.lock();
try {
- checkModifications();
- list.add(start + read.size, o);
- read = new SubListReadData(read.size + 1, list.getData());
- return true;
+ checkForComodification();
+ if (index<0 || index>size)
+ throw new IndexOutOfBoundsException();
+ l.add(index+offset, element);
+ expectedArray = l.getArray();
+ size++;
} finally {
- list.lock.unlock();
+ lock.unlock();
}
}
- public boolean addAll(Collection c) {
- list.lock.lock();
+ public void clear() {
+ final ReentrantLock lock = l.lock;
+ lock.lock();
try {
- checkModifications();
- int d = list.size();
- list.addAll(start + read.size, c);
- read = new SubListReadData(read.size + (list.size() - d), list
- .getData());
- return true;
+ checkForComodification();
+ l.removeRange(offset, offset+size);
+ expectedArray = l.getArray();
+ size = 0;
} finally {
- list.lock.unlock();
+ lock.unlock();
}
}
- public void clear() {
- list.lock.lock();
+ public E remove(int index) {
+ final ReentrantLock lock = l.lock;
+ lock.lock();
try {
- checkModifications();
- list.removeRange(start, read.size);
- read = new SubListReadData(0, list.getData());
+ rangeCheck(index);
+ checkForComodification();
+ E result = l.remove(index+offset);
+ expectedArray = l.getArray();
+ size--;
+ return result;
} finally {
- list.lock.unlock();
+ lock.unlock();
}
}
- public boolean contains(Object o) {
- return indexOf(o) != -1;
- }
-
- public boolean containsAll(Collection c) {
- SubListReadData b = read;
- return CopyOnWriteArrayList.containsAll(c, b.data, start, b.size);
- }
-
- public int indexOf(Object o) {
- SubListReadData b = read;
- int ind = CopyOnWriteArrayList.indexOf(o, b.data, start, b.size)
- - start;
- return ind < 0 ? -1 : ind;
- }
-
- public boolean isEmpty() {
- return read.size == 0;
- }
-
- public Iterator iterator() {
- return new SubListIterator(0, read);
- }
-
- public int lastIndexOf(Object o) {
- SubListReadData b = read;
- int ind = CopyOnWriteArrayList
- .lastIndexOf(o, b.data, start, b.size)
- - start;
- return ind < 0 ? -1 : ind;
- }
-
- public ListIterator listIterator() {
- return new SubListIterator(0, read);
+ public boolean remove(Object o) {
+ int index = indexOf(o);
+ if (index == -1)
+ return false;
+ remove(index);
+ return true;
}
- public boolean remove(Object o) {
- list.lock.lock();
+ public Iterator<E> iterator() {
+ final ReentrantLock lock = l.lock;
+ lock.lock();
try {
- checkModifications();
- int i = indexOf(o);
- if (i == -1) {
- return false;
- }
- boolean result = list.remove(i + start) != null;
- if (result) {
- read = new SubListReadData(read.size - 1, list.getData());
- }
- return result;
+ checkForComodification();
+ return new COWSubListIterator<E>(l, 0, offset, size);
} finally {
- list.lock.unlock();
+ lock.unlock();
}
}
- public boolean removeAll(Collection c) {
- list.lock.lock();
+ public ListIterator<E> listIterator(final int index) {
+ final ReentrantLock lock = l.lock;
+ lock.lock();
try {
- checkModifications();
- int removed = list.removeAll(c, start, read.size);
- if (removed > 0) {
- read = new SubListReadData(read.size - removed, list
- .getData());
- return true;
- }
+ checkForComodification();
+ if (index<0 || index>size)
+ throw new IndexOutOfBoundsException("Index: "+index+
+ ", Size: "+size);
+ return new COWSubListIterator<E>(l, index, offset, size);
} finally {
- list.lock.unlock();
+ lock.unlock();
}
- return false;
}
- public boolean retainAll(Collection c) {
- list.lock.lock();
+ public List<E> subList(int fromIndex, int toIndex) {
+ final ReentrantLock lock = l.lock;
+ lock.lock();
try {
- checkModifications();
- int removed = list.retainAll(c, start, read.size);
- if (removed > 0) {
- read = new SubListReadData(read.size - removed, list
- .getData());
- return true;
- }
- return false;
+ checkForComodification();
+ if (fromIndex<0 || toIndex>size)
+ throw new IndexOutOfBoundsException();
+ return new COWSubList<E>(l, fromIndex + offset,
+ toIndex + offset);
} finally {
- list.lock.unlock();
+ lock.unlock();
}
}
- public List subList(int fromIndex, int toIndex) {
- return new SubList(list, start + fromIndex, start + toIndex);
- }
+ }
+
- public Object[] toArray() {
- SubListReadData r = read;
- return CopyOnWriteArrayList.toArray(r.data, start, r.size);
+ private static class COWSubListIterator<E> implements ListIterator<E> {
+ private final ListIterator<E> i;
+ private final int index;
+ private final int offset;
+ private final int size;
+
+ COWSubListIterator(List<E> l, int index, int offset,
+ int size) {
+ this.index = index;
+ this.offset = offset;
+ this.size = size;
+ i = l.listIterator(index+offset);
}
- public Object[] toArray(Object[] a) {
- SubListReadData r = read;
- return CopyOnWriteArrayList.toArray(a, r.data, start, r.size);
+ public boolean hasNext() {
+ return nextIndex() < size;
}
- /**
- * @see java.util.List#addAll(int, java.util.Collection)
- */
- public boolean addAll(int index, Collection collection) {
- list.lock.lock();
- try {
- checkIndexInclusive(index, read.size);
- checkModifications();
- int d = list.size();
- boolean rt = list.addAll(index + start, collection);
- read = new SubListReadData(read.size + list.size() - d, list
- .getData());
- return rt;
- } finally {
- list.lock.unlock();
- }
+ public E next() {
+ if (hasNext())
+ return i.next();
+ else
+ throw new NoSuchElementException();
}
- /**
- * Implementation of <code>ListIterator</code> for the
- * <code>SubList</code>
- * gets a snapshot of the sublist,
- * never throws ConcurrentModificationException
- */
- private class SubListIterator extends ListIteratorImpl {
- private final SubListReadData dataR;
-
- /**
- * Constructs an iterator starting with the given index
- *
- * @param index index of the first element to iterate.
- */
- private SubListIterator(int index, SubListReadData d) {
- super(d.data, index + start);
- this.dataR = d;
- }
+ public boolean hasPrevious() {
+ return previousIndex() >= 0;
+ }
- /**
- * @see java.util.ListIterator#nextIndex()
- */
- public int nextIndex() {
- return super.nextIndex() - start;
- }
+ public E previous() {
+ if (hasPrevious())
+ return i.previous();
+ else
+ throw new NoSuchElementException();
+ }
- /**
- * @see java.util.ListIterator#previousIndex()
- */
- public int previousIndex() {
- return super.previousIndex() - start;
- }
+ public int nextIndex() {
+ return i.nextIndex() - offset;
+ }
- /**
- * @see java.util.Iterator#hasNext()
- */
- public boolean hasNext() {
- return nextIndex() < dataR.size;
- }
+ public int previousIndex() {
+ return i.previousIndex() - offset;
+ }
- /**
- * @see java.util.ListIterator#hasPrevious()
- */
- public boolean hasPrevious() {
- return previousIndex() > -1;
- }
+ public void remove() {
+ throw new UnsupportedOperationException();
}
- }
+ public void set(E e) {
+ throw new UnsupportedOperationException();
+ }
- //serialization support
- /**
- * Writes the object state to the ObjectOutputStream.
- *
- * @param oos ObjectOutputStream to write object to.
- * @throws IOException if an I/O error occur.
- */
- private void writeObject(ObjectOutputStream oos) throws IOException {
- E[] back = getData();
- int size = back.length;
- oos.defaultWriteObject();
- oos.writeInt(size);
- for (int i = 0; i < size; i++) {
- oos.writeObject(back[i]);
+ public void add(E e) {
+ throw new UnsupportedOperationException();
}
}
- /**
- * Reads the object state from the ObjectOutputStream.
- *
- * @param ois ObjectInputStream to read object from.
- * @throws IOException if an I/O error occur.
- */
- private void readObject(ObjectInputStream ois) throws IOException,
- ClassNotFoundException {
- ois.defaultReadObject();
- int length = ois.readInt();
- if (length == 0) {
- setData(newElementArray(0));
- } else {
- E[] back = newElementArray(length);
- for (int i = 0; i < back.length; i++) {
- back[i] = (E) ois.readObject();
- }
- setData(back);
- }
+ // Support for resetting lock while deserializing
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+ private static final long lockOffset;
+ static {
+ try {
+ lockOffset = unsafe.objectFieldOffset
+ (CopyOnWriteArrayList.class.getDeclaredField("lock"));
+ } catch (Exception ex) { throw new Error(ex); }
+ }
+ private void resetLock() {
+ unsafe.putObjectVolatile(this, lockOffset, new ReentrantLock());
}
}
diff --git a/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java b/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java
index 532952b..65a05de 100644
--- a/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java
+++ b/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java
@@ -1,7 +1,7 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
- * Expert Group and released to the public domain. Use, modify, and
- * redistribute this code in any way without acknowledgement.
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/licenses/publicdomain
*/
package java.util.concurrent;
@@ -9,39 +9,37 @@ import java.util.*;
// BEGIN android-note
// removed link to collections framework docs
-// Removed clonable interface to be closer to the RI.
// END android-note
/**
- * A {@link java.util.Set} that uses {@link
- * java.util.concurrent.CopyOnWriteArrayList} for all of its
- * operations. Thus, it shares the same basic properties:
+ * A {@link java.util.Set} that uses an internal {@link CopyOnWriteArrayList}
+ * for all of its operations. Thus, it shares the same basic properties:
* <ul>
* <li>It is best suited for applications in which set sizes generally
* stay small, read-only operations
* vastly outnumber mutative operations, and you need
* to prevent interference among threads during traversal.
- * <li>Mutative operations(add, set, remove, etc) are expensive
- * since they usually entail copying the entire underlying array.
- * <li>Iterators do not support the mutative remove operation
- * <li>Traversal via iterators is very fast and cannot ever encounter
+ * <li>It is thread-safe.
+ * <li>Mutative operations (<tt>add</tt>, <tt>set</tt>, <tt>remove</tt>, etc.)
+ * are expensive since they usually entail copying the entire underlying
+ * array.
+ * <li>Iterators do not support the mutative <tt>remove</tt> operation.
+ * <li>Traversal via iterators is fast and cannot encounter
* interference from other threads. Iterators rely on
* unchanging snapshots of the array at the time the iterators were
- * constructed.
+ * constructed.
* </ul>
- * <p>
- * <b>Sample Usage.</b> Probably the main application
- * of copy-on-write sets are classes that maintain
- * sets of Handler objects
- * that must be multicasted to upon an update command. This
- * is a classic case where you do not want to be holding a
- * lock while sending a message, and where traversals normally
- * vastly overwhelm additions.
+ *
+ * <p> <b>Sample Usage.</b> The following code sketch uses a
+ * copy-on-write set to maintain a set of Handler objects that
+ * perform some action upon state updates.
+ *
* <pre>
* class Handler { void handle(); ... }
*
* class X {
- * private final CopyOnWriteArraySet&lt;Handler&gt; handlers = new CopyOnWriteArraySet&lt;Handler&gt;();
+ * private final CopyOnWriteArraySet&lt;Handler&gt; handlers
+ * = new CopyOnWriteArraySet&lt;Handler&gt;();
* public void addHandler(Handler h) { handlers.add(h); }
*
* private long internalState;
@@ -49,14 +47,13 @@ import java.util.*;
*
* public void update() {
* changeState();
- * Iterator it = handlers.iterator();
- * while (it.hasNext())
- * it.next().handle();
+ * for (Handler handler : handlers)
+ * handler.handle();
* }
* }
* </pre>
- * @see CopyOnWriteArrayList
*
+ * @see CopyOnWriteArrayList
* @since 1.5
* @author Doug Lea
* @param <E> the type of elements held in this collection
@@ -76,27 +73,292 @@ public class CopyOnWriteArraySet<E> extends AbstractSet<E>
/**
* Creates a set containing all of the elements of the specified
- * Collection.
- * @param c the collection
+ * collection.
+ *
+ * @param c the collection of elements to initially contain
+ * @throws NullPointerException if the specified collection is null
*/
public CopyOnWriteArraySet(Collection<? extends E> c) {
al = new CopyOnWriteArrayList<E>();
al.addAllAbsent(c);
}
+ /**
+ * Returns the number of elements in this set.
+ *
+ * @return the number of elements in this set
+ */
+ public int size() {
+ return al.size();
+ }
+
+ /**
+ * Returns <tt>true</tt> if this set contains no elements.
+ *
+ * @return <tt>true</tt> if this set contains no elements
+ */
+ public boolean isEmpty() {
+ return al.isEmpty();
+ }
+
+ /**
+ * Returns <tt>true</tt> if this set contains the specified element.
+ * More formally, returns <tt>true</tt> if and only if this set
+ * contains an element <tt>e</tt> such that
+ * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
+ *
+ * @param o element whose presence in this set is to be tested
+ * @return <tt>true</tt> if this set contains the specified element
+ */
+ public boolean contains(Object o) {
+ return al.contains(o);
+ }
+
+ /**
+ * Returns an array containing all of the elements in this set.
+ * If this set makes any guarantees as to what order its elements
+ * are returned by its iterator, this method must return the
+ * elements in the same order.
+ *
+ * <p>The returned array will be "safe" in that no references to it
+ * are maintained by this set. (In other words, this method must
+ * allocate a new array even if this set is backed by an 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 the elements in this set
+ */
+ public Object[] toArray() {
+ return al.toArray();
+ }
+
+ /**
+ * Returns an array containing all of the elements in this set; the
+ * runtime type of the returned array is that of the specified array.
+ * If the set 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 set.
+ *
+ * <p>If this set fits in the specified array with room to spare
+ * (i.e., the array has more elements than this set), the element in
+ * the array immediately following the end of the set is set to
+ * <tt>null</tt>. (This is useful in determining the length of this
+ * set <i>only</i> if the caller knows that this set does not contain
+ * any null elements.)
+ *
+ * <p>If this set makes any guarantees as to what order its elements
+ * are returned by its iterator, this method must return the elements
+ * in the same order.
+ *
+ * <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 set known to contain only strings.
+ * The following code can be used to dump the set 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 this set 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 the elements in this set
+ * @throws ArrayStoreException if the runtime type of the specified array
+ * is not a supertype of the runtime type of every element in this
+ * set
+ * @throws NullPointerException if the specified array is null
+ */
+ public <T> T[] toArray(T[] a) {
+ return al.toArray(a);
+ }
+
+ /**
+ * Removes all of the elements from this set.
+ * The set will be empty after this call returns.
+ */
+ public void clear() {
+ al.clear();
+ }
- public int size() { return al.size(); }
- public boolean isEmpty() { return al.isEmpty(); }
- public boolean contains(Object o) { return al.contains(o); }
- public Object[] toArray() { return al.toArray(); }
- public <T> T[] toArray(T[] a) { return al.toArray(a); }
- public void clear() { al.clear(); }
- public Iterator<E> iterator() { return al.iterator(); }
- public boolean remove(Object o) { return al.remove(o); }
- public boolean add(E o) { return al.addIfAbsent(o); }
- public boolean containsAll(Collection<?> c) { return al.containsAll(c); }
- public boolean addAll(Collection<? extends E> c) { return al.addAllAbsent(c) > 0; }
- public boolean removeAll(Collection<?> c) { return al.removeAll(c); }
- public boolean retainAll(Collection<?> c) { return al.retainAll(c); }
+ /**
+ * Removes the specified element from this set if it is present.
+ * More formally, removes an element <tt>e</tt> such that
+ * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>,
+ * if this set contains such an element. Returns <tt>true</tt> if
+ * this set contained the element (or equivalently, if this set
+ * changed as a result of the call). (This set will not contain the
+ * element once the call returns.)
+ *
+ * @param o object to be removed from this set, if present
+ * @return <tt>true</tt> if this set contained the specified element
+ */
+ public boolean remove(Object o) {
+ return al.remove(o);
+ }
+ /**
+ * Adds the specified element to this set if it is not already present.
+ * More formally, adds the specified element <tt>e</tt> to this set if
+ * the set contains no element <tt>e2</tt> such that
+ * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
+ * If this set already contains the element, the call leaves the set
+ * unchanged and returns <tt>false</tt>.
+ *
+ * @param e element to be added to this set
+ * @return <tt>true</tt> if this set did not already contain the specified
+ * element
+ */
+ public boolean add(E e) {
+ return al.addIfAbsent(e);
+ }
+
+ /**
+ * Returns <tt>true</tt> if this set contains all of the elements of the
+ * specified collection. If the specified collection is also a set, this
+ * method returns <tt>true</tt> if it is a <i>subset</i> of this set.
+ *
+ * @param c collection to be checked for containment in this set
+ * @return <tt>true</tt> if this set contains all of the elements of the
+ * specified collection
+ * @throws NullPointerException if the specified collection is null
+ * @see #contains(Object)
+ */
+ public boolean containsAll(Collection<?> c) {
+ return al.containsAll(c);
+ }
+
+ /**
+ * Adds all of the elements in the specified collection to this set if
+ * they're not already present. If the specified collection is also a
+ * set, the <tt>addAll</tt> operation effectively modifies this set so
+ * that its value is the <i>union</i> of the two sets. The behavior of
+ * this operation is undefined if the specified collection is modified
+ * while the operation is in progress.
+ *
+ * @param c collection containing elements to be added to this set
+ * @return <tt>true</tt> if this set changed as a result of the call
+ * @throws NullPointerException if the specified collection is null
+ * @see #add(Object)
+ */
+ public boolean addAll(Collection<? extends E> c) {
+ return al.addAllAbsent(c) > 0;
+ }
+
+ /**
+ * Removes from this set all of its elements that are contained in the
+ * specified collection. If the specified collection is also a set,
+ * this operation effectively modifies this set so that its value is the
+ * <i>asymmetric set difference</i> of the two sets.
+ *
+ * @param c collection containing elements to be removed from this set
+ * @return <tt>true</tt> if this set changed as a result of the call
+ * @throws ClassCastException if the class of an element of this set
+ * is incompatible with the specified collection (optional)
+ * @throws NullPointerException if this set contains a null element and the
+ * specified collection does not permit null elements (optional),
+ * or if the specified collection is null
+ * @see #remove(Object)
+ */
+ public boolean removeAll(Collection<?> c) {
+ return al.removeAll(c);
+ }
+
+ /**
+ * Retains only the elements in this set that are contained in the
+ * specified collection. In other words, removes from this set all of
+ * its elements that are not contained in the specified collection. If
+ * the specified collection is also a set, this operation effectively
+ * modifies this set so that its value is the <i>intersection</i> of the
+ * two sets.
+ *
+ * @param c collection containing elements to be retained in this set
+ * @return <tt>true</tt> if this set changed as a result of the call
+ * @throws ClassCastException if the class of an element of this set
+ * is incompatible with the specified collection (optional)
+ * @throws NullPointerException if this set contains a null element and the
+ * specified collection does not permit null elements (optional),
+ * or if the specified collection is null
+ * @see #remove(Object)
+ */
+ public boolean retainAll(Collection<?> c) {
+ return al.retainAll(c);
+ }
+
+ /**
+ * Returns an iterator over the elements contained in this set
+ * in the order in which these elements were added.
+ *
+ * <p>The returned iterator provides a snapshot of the state of the set
+ * when the iterator was constructed. No synchronization is needed while
+ * traversing the iterator. The iterator does <em>NOT</em> support the
+ * <tt>remove</tt> method.
+ *
+ * @return an iterator over the elements in this set
+ */
+ public Iterator<E> iterator() {
+ return al.iterator();
+ }
+
+ /**
+ * Compares the specified object with this set for equality.
+ * Returns {@code true} if the specified object is the same object
+ * as this object, or if it is also a {@link Set} and the elements
+ * returned by an {@linkplain List#iterator() iterator} over the
+ * specified set are the same as the elements returned by an
+ * iterator over this set. More formally, the two iterators are
+ * considered to return the same elements if they return the same
+ * number of elements and for every element {@code e1} returned by
+ * the iterator over the specified set, there is an element
+ * {@code e2} returned by the iterator over this set such that
+ * {@code (e1==null ? e2==null : e1.equals(e2))}.
+ *
+ * @param o object to be compared for equality with this set
+ * @return {@code true} if the specified object is equal to this set
+ */
+ public boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (!(o instanceof Set))
+ return false;
+ Set<?> set = (Set<?>)(o);
+ Iterator<?> it = set.iterator();
+
+ // Uses O(n^2) algorithm that is only appropriate
+ // for small sets, which CopyOnWriteArraySets should be.
+
+ // Use a single snapshot of underlying array
+ Object[] elements = al.getArray();
+ int len = elements.length;
+ // Mark matched elements to avoid re-checking
+ boolean[] matched = new boolean[len];
+ int k = 0;
+ outer: while (it.hasNext()) {
+ if (++k > len)
+ return false;
+ Object x = it.next();
+ for (int i = 0; i < len; ++i) {
+ if (!matched[i] && eq(x, elements[i])) {
+ matched[i] = true;
+ continue outer;
+ }
+ }
+ return false;
+ }
+ return k == len;
+ }
+
+ /**
+ * Test for equality, coping with nulls.
+ */
+ private static boolean eq(Object o1, Object o2) {
+ return (o1 == null ? o2 == null : o1.equals(o2));
+ }
}
diff --git a/concurrent/src/main/java/java/util/concurrent/CountDownLatch.java b/concurrent/src/main/java/java/util/concurrent/CountDownLatch.java
index 721744c..2b945dd 100644
--- a/concurrent/src/main/java/java/util/concurrent/CountDownLatch.java
+++ b/concurrent/src/main/java/java/util/concurrent/CountDownLatch.java
@@ -12,25 +12,25 @@ import java.util.concurrent.atomic.*;
* A synchronization aid that allows one or more threads to wait until
* a set of operations being performed in other threads completes.
*
- * <p>A <tt>CountDownLatch</tt> is initialized with a given
- * <em>count</em>. The {@link #await await} methods block until the current
- * {@link #getCount count} reaches zero due to invocations of the
- * {@link #countDown} method, after which all waiting threads are
- * released and any subsequent invocations of {@link #await await} return
- * immediately. This is a one-shot phenomenon -- the count cannot be
- * reset. If you need a version that resets the count, consider using
- * a {@link CyclicBarrier}.
+ * <p>A {@code CountDownLatch} is initialized with a given <em>count</em>.
+ * The {@link #await await} methods block until the current count reaches
+ * zero due to invocations of the {@link #countDown} method, after which
+ * all waiting threads are released and any subsequent invocations of
+ * {@link #await await} return immediately. This is a one-shot phenomenon
+ * -- the count cannot be reset. If you need a version that resets the
+ * count, consider using a {@link CyclicBarrier}.
*
- * <p>A <tt>CountDownLatch</tt> is a versatile synchronization tool
+ * <p>A {@code CountDownLatch} is a versatile synchronization tool
* and can be used for a number of purposes. A
- * <tt>CountDownLatch</tt> initialized with a count of one serves as a
+ * {@code CountDownLatch} initialized with a count of one serves as a
* simple on/off latch, or gate: all threads invoking {@link #await await}
* wait at the gate until it is opened by a thread invoking {@link
- * #countDown}. A <tt>CountDownLatch</tt> initialized to <em>N</em>
+ * #countDown}. A {@code CountDownLatch} initialized to <em>N</em>
* can be used to make one thread wait until <em>N</em> threads have
* completed some action, or some action has been completed N times.
- * <p>A useful property of a <tt>CountDownLatch</tt> is that it
- * doesn't require that threads calling <tt>countDown</tt> wait for
+ *
+ * <p>A useful property of a {@code CountDownLatch} is that it
+ * doesn't require that threads calling {@code countDown} wait for
* the count to reach zero before proceeding, it simply prevents any
* thread from proceeding past an {@link #await await} until all
* threads could pass.
@@ -119,6 +119,13 @@ import java.util.concurrent.atomic.*;
*
* </pre>
*
+ * <p>Memory consistency effects: Until the count reaches
+ * zero, actions in a thread prior to calling
+ * {@code countDown()}
+ * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
+ * actions following a successful return from a corresponding
+ * {@code await()} in another thread.
+ *
* @since 1.5
* @author Doug Lea
*/
@@ -128,118 +135,120 @@ public class CountDownLatch {
* Uses AQS state to represent count.
*/
private static final class Sync extends AbstractQueuedSynchronizer {
+ private static final long serialVersionUID = 4982264981922014374L;
+
Sync(int count) {
- setState(count);
+ setState(count);
}
-
+
int getCount() {
return getState();
}
- public int tryAcquireShared(int acquires) {
+ protected int tryAcquireShared(int acquires) {
return getState() == 0? 1 : -1;
}
-
- public boolean tryReleaseShared(int releases) {
+
+ protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
- if (compareAndSetState(c, nextc))
+ if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
private final Sync sync;
+
/**
- * Constructs a <tt>CountDownLatch</tt> initialized with the given
- * count.
- *
- * @param count the number of times {@link #countDown} must be invoked
- * before threads can pass through {@link #await}.
+ * Constructs a {@code CountDownLatch} initialized with the given count.
*
- * @throws IllegalArgumentException if <tt>count</tt> is less than zero.
+ * @param count the number of times {@link #countDown} must be invoked
+ * before threads can pass through {@link #await}
+ * @throws IllegalArgumentException if {@code count} is negative
*/
- public CountDownLatch(int count) {
+ public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
/**
- * Causes the current thread to wait until the latch has counted down to
- * zero, unless the thread is {@link Thread#interrupt interrupted}.
+ * Causes the current thread to wait until the latch has counted down to
+ * zero, unless the thread is {@linkplain Thread#interrupt interrupted}.
*
- * <p>If the current {@link #getCount count} is zero then this method
- * returns immediately.
- * <p>If the current {@link #getCount count} is greater than zero then
- * the current thread becomes disabled for thread scheduling
- * purposes and lies dormant until one of two things happen:
+ * <p>If the current count is zero then this method returns immediately.
+ *
+ * <p>If the current count is greater than zero then the current
+ * thread becomes disabled for thread scheduling purposes and lies
+ * dormant until one of two things happen:
* <ul>
* <li>The count reaches zero due to invocations of the
* {@link #countDown} method; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread.
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
+ * the current thread.
* </ul>
+ *
* <p>If the current thread:
* <ul>
- * <li>has its interrupted status set on entry to this method; or
- * <li>is {@link Thread#interrupt interrupted} while waiting,
+ * <li>has its interrupted status set on entry to this method; or
+ * <li>is {@linkplain Thread#interrupt interrupted} while waiting,
* </ul>
- * then {@link InterruptedException} is thrown and the current thread's
- * interrupted status is cleared.
+ * then {@link InterruptedException} is thrown and the current thread's
+ * interrupted status is cleared.
*
* @throws InterruptedException if the current thread is interrupted
- * while waiting.
+ * while waiting
*/
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
/**
- * Causes the current thread to wait until the latch has counted down to
- * zero, unless the thread is {@link Thread#interrupt interrupted},
+ * Causes the current thread to wait until the latch has counted down to
+ * zero, unless the thread is {@linkplain Thread#interrupt interrupted},
* or the specified waiting time elapses.
*
- * <p>If the current {@link #getCount count} is zero then this method
- * returns immediately with the value <tt>true</tt>.
+ * <p>If the current count is zero then this method returns immediately
+ * with the value {@code true}.
*
- * <p>If the current {@link #getCount count} is greater than zero then
- * the current thread becomes disabled for thread scheduling
- * purposes and lies dormant until one of three things happen:
+ * <p>If the current count is greater than zero then the current
+ * thread becomes disabled for thread scheduling purposes and lies
+ * dormant until one of three things happen:
* <ul>
* <li>The count reaches zero due to invocations of the
* {@link #countDown} method; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread; or
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
+ * the current thread; or
* <li>The specified waiting time elapses.
* </ul>
+ *
* <p>If the count reaches zero then the method returns with the
- * value <tt>true</tt>.
+ * value {@code true}.
+ *
* <p>If the current thread:
* <ul>
- * <li>has its interrupted status set on entry to this method; or
- * <li>is {@link Thread#interrupt interrupted} while waiting,
+ * <li>has its interrupted status set on entry to this method; or
+ * <li>is {@linkplain Thread#interrupt interrupted} while waiting,
* </ul>
- * then {@link InterruptedException} is thrown and the current thread's
- * interrupted status is cleared.
+ * then {@link InterruptedException} is thrown and the current thread's
+ * interrupted status is cleared.
*
- * <p>If the specified waiting time elapses then the value <tt>false</tt>
- * is returned.
- * If the time is
- * less than or equal to zero, the method will not wait at all.
+ * <p>If the specified waiting time elapses then the value {@code false}
+ * is returned. If the time is less than or equal to zero, the method
+ * will not wait at all.
*
* @param timeout the maximum time to wait
- * @param unit the time unit of the <tt>timeout</tt> argument.
- * @return <tt>true</tt> if the count reached zero and <tt>false</tt>
- * if the waiting time elapsed before the count reached zero.
- *
+ * @param unit the time unit of the {@code timeout} argument
+ * @return {@code true} if the count reached zero and {@code false}
+ * if the waiting time elapsed before the count reached zero
* @throws InterruptedException if the current thread is interrupted
- * while waiting.
+ * while waiting
*/
- public boolean await(long timeout, TimeUnit unit)
+ public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
@@ -247,11 +256,12 @@ public class CountDownLatch {
/**
* Decrements the count of the latch, releasing all waiting threads if
* the count reaches zero.
- * <p>If the current {@link #getCount count} is greater than zero then
- * it is decremented. If the new count is zero then all waiting threads
- * are re-enabled for thread scheduling purposes.
- * <p>If the current {@link #getCount count} equals zero then nothing
- * happens.
+ *
+ * <p>If the current count is greater than zero then it is decremented.
+ * If the new count is zero then all waiting threads are re-enabled for
+ * thread scheduling purposes.
+ *
+ * <p>If the current count equals zero then nothing happens.
*/
public void countDown() {
sync.releaseShared(1);
@@ -259,8 +269,10 @@ public class CountDownLatch {
/**
* Returns the current count.
+ *
* <p>This method is typically used for debugging and testing purposes.
- * @return the current count.
+ *
+ * @return the current count
*/
public long getCount() {
return sync.getCount();
@@ -268,13 +280,12 @@ public class CountDownLatch {
/**
* Returns a string identifying this latch, as well as its state.
- * The state, in brackets, includes the String
- * &quot;Count =&quot; followed by the current count.
- * @return a string identifying this latch, as well as its
- * state
+ * The state, in brackets, includes the String {@code "Count ="}
+ * followed by the current count.
+ *
+ * @return a string identifying this latch, as well as its state
*/
public String toString() {
return super.toString() + "[Count = " + sync.getCount() + "]";
}
-
}
diff --git a/concurrent/src/main/java/java/util/concurrent/CyclicBarrier.java b/concurrent/src/main/java/java/util/concurrent/CyclicBarrier.java
index d70c4c7..d5738c5 100644
--- a/concurrent/src/main/java/java/util/concurrent/CyclicBarrier.java
+++ b/concurrent/src/main/java/java/util/concurrent/CyclicBarrier.java
@@ -17,10 +17,10 @@ import java.util.concurrent.locks.*;
*
* <p>A <tt>CyclicBarrier</tt> supports an optional {@link Runnable} command
* that is run once per barrier point, after the last thread in the party
- * arrives, but before any threads are released.
+ * arrives, but before any threads are released.
* This <em>barrier action</em> is useful
* for updating shared-state before any of the parties continue.
- *
+ *
* <p><b>Sample usage:</b> Here is an example of
* using a barrier in a parallel decomposition design:
* <pre>
@@ -28,7 +28,7 @@ import java.util.concurrent.locks.*;
* final int N;
* final float[][] data;
* final CyclicBarrier barrier;
- *
+ *
* class Worker implements Runnable {
* int myRow;
* Worker(int row) { myRow = row; }
@@ -37,11 +37,11 @@ import java.util.concurrent.locks.*;
* processRow(myRow);
*
* try {
- * barrier.await();
- * } catch (InterruptedException ex) {
- * return;
- * } catch (BrokenBarrierException ex) {
- * return;
+ * barrier.await();
+ * } catch (InterruptedException ex) {
+ * return;
+ * } catch (BrokenBarrierException ex) {
+ * return;
* }
* }
* }
@@ -50,22 +50,22 @@ import java.util.concurrent.locks.*;
* public Solver(float[][] matrix) {
* data = matrix;
* N = matrix.length;
- * barrier = new CyclicBarrier(N,
+ * barrier = new CyclicBarrier(N,
* new Runnable() {
- * public void run() {
- * mergeRows(...);
+ * public void run() {
+ * mergeRows(...);
* }
* });
- * for (int i = 0; i < N; ++i)
+ * for (int i = 0; i < N; ++i)
* new Thread(new Worker(i)).start();
*
* waitUntilDone();
* }
* }
* </pre>
- * Here, each worker thread processes a row of the matrix then waits at the
+ * Here, each worker thread processes a row of the matrix then waits at the
* barrier until all rows have been processed. When all rows are processed
- * the supplied {@link Runnable} barrier action is executed and merges the
+ * the supplied {@link Runnable} barrier action is executed and merges the
* rows. If the merger
* determines that a solution has been found then <tt>done()</tt> will return
* <tt>true</tt> and each worker will terminate.
@@ -74,19 +74,26 @@ import java.util.concurrent.locks.*;
* it is executed, then any of the threads in the party could execute that
* action when it is released. To facilitate this, each invocation of
* {@link #await} returns the arrival index of that thread at the barrier.
- * You can then choose which thread should execute the barrier action, for
+ * You can then choose which thread should execute the barrier action, for
* example:
* <pre> if (barrier.await() == 0) {
* // log the completion of this iteration
* }</pre>
*
- * <p>The <tt>CyclicBarrier</tt> uses a fast-fail all-or-none breakage
- * model for failed synchronization attempts: If a thread leaves a
- * barrier point prematurely because of interruption, failure, or
- * timeout, all other threads, even those that have not yet resumed
- * from a previous {@link #await}. will also leave abnormally via
- * {@link BrokenBarrierException} (or <tt>InterruptedException</tt> if
- * they too were interrupted at about the same time).
+ * <p>The <tt>CyclicBarrier</tt> uses an all-or-none breakage model
+ * for failed synchronization attempts: If a thread leaves a barrier
+ * point prematurely because of interruption, failure, or timeout, all
+ * other threads waiting at that barrier point will also leave
+ * abnormally via {@link BrokenBarrierException} (or
+ * {@link InterruptedException} if they too were interrupted at about
+ * the same time).
+ *
+ * <p>Memory consistency effects: Actions in a thread prior to calling
+ * {@code await()}
+ * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
+ * actions that are part of the barrier action, which in turn
+ * <i>happen-before</i> actions following a successful return from the
+ * corresponding {@code await()} in other threads.
*
* @since 1.5
* @see CountDownLatch
@@ -94,6 +101,21 @@ import java.util.concurrent.locks.*;
* @author Doug Lea
*/
public class CyclicBarrier {
+ /**
+ * Each use of the barrier is represented as a generation instance.
+ * The generation changes whenever the barrier is tripped, or
+ * is reset. There can be many generations associated with threads
+ * using the barrier - due to the non-deterministic way the lock
+ * may be allocated to waiting threads - but only one of these
+ * can be active at a time (the one to which <tt>count</tt> applies)
+ * and all the rest are either broken or tripped.
+ * There need not be an active generation if there has been a break
+ * but no subsequent reset.
+ */
+ private static class Generation {
+ boolean broken = false;
+ }
+
/** The lock for guarding barrier entry */
private final ReentrantLock lock = new ReentrantLock();
/** Condition to wait on until tripped */
@@ -102,53 +124,50 @@ public class CyclicBarrier {
private final int parties;
/* The command to run when tripped */
private final Runnable barrierCommand;
-
- /**
- * The generation number. Incremented upon barrier trip.
- * Retracted upon reset.
- */
- private long generation;
-
- /**
- * Breakage indicator.
- */
- private boolean broken;
+ /** The current generation */
+ private Generation generation = new Generation();
/**
* Number of parties still waiting. Counts down from parties to 0
- * on each cycle.
+ * on each generation. It is reset to parties on each new
+ * generation or when broken.
*/
- private int count;
+ private int count;
/**
- * Updates state on barrier trip and wake up everyone.
- */
+ * Updates state on barrier trip and wakes up everyone.
+ * Called only while holding lock.
+ */
private void nextGeneration() {
- count = parties;
- ++generation;
+ // signal completion of last generation
trip.signalAll();
+ // set up next generation
+ count = parties;
+ generation = new Generation();
}
/**
- * Sets barrier as broken and wake up everyone
+ * Sets current barrier generation as broken and wakes up everyone.
+ * Called only while holding lock.
*/
private void breakBarrier() {
- broken = true;
+ generation.broken = true;
+ count = parties;
trip.signalAll();
}
/**
* Main barrier code, covering the various policies.
*/
- private int dowait(boolean timed, long nanos)
- throws InterruptedException, BrokenBarrierException, TimeoutException {
+ private int dowait(boolean timed, long nanos)
+ throws InterruptedException, BrokenBarrierException,
+ TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
- int index = --count;
- long g = generation;
+ final Generation g = generation;
- if (broken)
+ if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
@@ -156,37 +175,45 @@ public class CyclicBarrier {
throw new InterruptedException();
}
- if (index == 0) { // tripped
- nextGeneration();
- boolean ranAction = false;
- try {
- Runnable command = barrierCommand;
- if (command != null)
- command.run();
- ranAction = true;
- return 0;
- } finally {
- if (!ranAction)
- breakBarrier();
- }
- }
+ int index = --count;
+ if (index == 0) { // tripped
+ boolean ranAction = false;
+ try {
+ final Runnable command = barrierCommand;
+ if (command != null)
+ command.run();
+ ranAction = true;
+ nextGeneration();
+ return 0;
+ } finally {
+ if (!ranAction)
+ breakBarrier();
+ }
+ }
+ // loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
- if (!timed)
+ if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
- breakBarrier();
- throw ie;
+ if (g == generation && ! g.broken) {
+ breakBarrier();
+ throw ie;
+ } else {
+ // We're about to finish waiting even if we had not
+ // been interrupted, so this interrupt is deemed to
+ // "belong" to subsequent execution.
+ Thread.currentThread().interrupt();
+ }
}
-
- if (broken ||
- g > generation) // true if a reset occurred while waiting
+
+ if (g.broken)
throw new BrokenBarrierException();
- if (g < generation)
+ if (g != generation)
return index;
if (timed && nanos <= 0L) {
@@ -194,7 +221,6 @@ public class CyclicBarrier {
throw new TimeoutException();
}
}
-
} finally {
lock.unlock();
}
@@ -207,15 +233,14 @@ public class CyclicBarrier {
* performed by the last thread entering the barrier.
*
* @param parties the number of threads that must invoke {@link #await}
- * before the barrier is tripped.
+ * before the barrier is tripped
* @param barrierAction the command to execute when the barrier is
- * tripped, or <tt>null</tt> if there is no action.
- *
- * @throws IllegalArgumentException if <tt>parties</tt> is less than 1.
+ * tripped, or {@code null} if there is no action
+ * @throws IllegalArgumentException if {@code parties} is less than 1
*/
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
- this.parties = parties;
+ this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
@@ -223,12 +248,11 @@ public class CyclicBarrier {
/**
* Creates a new <tt>CyclicBarrier</tt> that will trip when the
* given number of parties (threads) are waiting upon it, and
- * does not perform a predefined action upon each barrier.
+ * does not perform a predefined action when the barrier is tripped.
*
* @param parties the number of threads that must invoke {@link #await}
- * before the barrier is tripped.
- *
- * @throws IllegalArgumentException if <tt>parties</tt> is less than 1.
+ * before the barrier is tripped
+ * @throws IllegalArgumentException if {@code parties} is less than 1
*/
public CyclicBarrier(int parties) {
this(parties, null);
@@ -236,64 +260,66 @@ public class CyclicBarrier {
/**
* Returns the number of parties required to trip this barrier.
- * @return the number of parties required to trip this barrier.
- **/
+ *
+ * @return the number of parties required to trip this barrier
+ */
public int getParties() {
return parties;
}
/**
- * Waits until all {@link #getParties parties} have invoked <tt>await</tt>
- * on this barrier.
+ * Waits until all {@linkplain #getParties parties} have invoked
+ * <tt>await</tt> on this barrier.
*
* <p>If the current thread is not the last to arrive then it is
* disabled for thread scheduling purposes and lies dormant until
- * one of following things happens:
+ * one of the following things happens:
* <ul>
* <li>The last thread arrives; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread; or
- * <li>Some other thread {@link Thread#interrupt interrupts} one of the
- * other waiting threads; or
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
+ * the current thread; or
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
+ * one of the other waiting threads; or
* <li>Some other thread times out while waiting for barrier; or
* <li>Some other thread invokes {@link #reset} on this barrier.
* </ul>
+ *
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
- * <li>is {@link Thread#interrupt interrupted} while waiting
+ * <li>is {@linkplain Thread#interrupt interrupted} while waiting
* </ul>
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
- * <p>If the barrier is {@link #reset} while any thread is waiting, or if
- * the barrier {@link #isBroken is broken} when <tt>await</tt> is invoked,
- * or while any thread is waiting,
- * then {@link BrokenBarrierException} is thrown.
+ * <p>If the barrier is {@link #reset} while any thread is waiting,
+ * or if the barrier {@linkplain #isBroken is broken} when
+ * <tt>await</tt> is invoked, or while any thread is waiting, then
+ * {@link BrokenBarrierException} is thrown.
*
- * <p>If any thread is {@link Thread#interrupt interrupted} while waiting,
- * then all other waiting threads will throw
+ * <p>If any thread is {@linkplain Thread#interrupt interrupted} while waiting,
+ * then all other waiting threads will throw
* {@link BrokenBarrierException} and the barrier is placed in the broken
* state.
*
* <p>If the current thread is the last thread to arrive, and a
* non-null barrier action was supplied in the constructor, then the
- * current thread runs the action before allowing the other threads to
+ * current thread runs the action before allowing the other threads to
* continue.
* If an exception occurs during the barrier action then that exception
* will be propagated in the current thread and the barrier is placed in
* the broken state.
*
* @return the arrival index of the current thread, where index
- * <tt>{@link #getParties()} - 1</tt> indicates the first to arrive and
- * zero indicates the last to arrive.
- *
- * @throws InterruptedException if the current thread was interrupted
- * while waiting
+ * <tt>{@link #getParties()} - 1</tt> indicates the first
+ * to arrive and zero indicates the last to arrive
+ * @throws InterruptedException if the current thread was interrupted
+ * while waiting
* @throws BrokenBarrierException if <em>another</em> thread was
- * interrupted while the current thread was waiting, or the barrier was
- * reset, or the barrier was broken when <tt>await</tt> was called,
- * or the barrier action (if present) failed due an exception.
+ * interrupted or timed out while the current thread was
+ * waiting, or the barrier was reset, or the barrier was
+ * broken when {@code await} was called, or the barrier
+ * action (if present) failed due an exception.
*/
public int await() throws InterruptedException, BrokenBarrierException {
try {
@@ -304,8 +330,8 @@ public class CyclicBarrier {
}
/**
- * Waits until all {@link #getParties parties} have invoked <tt>await</tt>
- * on this barrier.
+ * Waits until all {@linkplain #getParties parties} have invoked
+ * <tt>await</tt> on this barrier, or the specified waiting time elapses.
*
* <p>If the current thread is not the last to arrive then it is
* disabled for thread scheduling purposes and lies dormant until
@@ -313,34 +339,39 @@ public class CyclicBarrier {
* <ul>
* <li>The last thread arrives; or
* <li>The specified timeout elapses; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread; or
- * <li>Some other thread {@link Thread#interrupt interrupts} one of the
- * other waiting threads; or
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
+ * the current thread; or
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
+ * one of the other waiting threads; or
* <li>Some other thread times out while waiting for barrier; or
* <li>Some other thread invokes {@link #reset} on this barrier.
* </ul>
+ *
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
- * <li>is {@link Thread#interrupt interrupted} while waiting
+ * <li>is {@linkplain Thread#interrupt interrupted} while waiting
* </ul>
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
- * <p>If the barrier is {@link #reset} while any thread is waiting, or if
- * the barrier {@link #isBroken is broken} when <tt>await</tt> is invoked,
- * or while any thread is waiting,
- * then {@link BrokenBarrierException} is thrown.
+ * <p>If the specified waiting time elapses then {@link TimeoutException}
+ * is thrown. If the time is less than or equal to zero, the
+ * method will not wait at all.
*
- * <p>If any thread is {@link Thread#interrupt interrupted} while waiting,
- * then all other waiting threads will throw
- * {@link BrokenBarrierException} and the barrier is placed in the broken
+ * <p>If the barrier is {@link #reset} while any thread is waiting,
+ * or if the barrier {@linkplain #isBroken is broken} when
+ * <tt>await</tt> is invoked, or while any thread is waiting, then
+ * {@link BrokenBarrierException} is thrown.
+ *
+ * <p>If any thread is {@linkplain Thread#interrupt interrupted} while
+ * waiting, then all other waiting threads will throw {@link
+ * BrokenBarrierException} and the barrier is placed in the broken
* state.
*
* <p>If the current thread is the last thread to arrive, and a
* non-null barrier action was supplied in the constructor, then the
- * current thread runs the action before allowing the other threads to
+ * current thread runs the action before allowing the other threads to
* continue.
* If an exception occurs during the barrier action then that exception
* will be propagated in the current thread and the barrier is placed in
@@ -349,36 +380,37 @@ public class CyclicBarrier {
* @param timeout the time to wait for the barrier
* @param unit the time unit of the timeout parameter
* @return the arrival index of the current thread, where index
- * <tt>{@link #getParties()} - 1</tt> indicates the first to arrive and
- * zero indicates the last to arrive.
- *
- * @throws InterruptedException if the current thread was interrupted
- * while waiting
- * @throws TimeoutException if the specified timeout elapses.
+ * <tt>{@link #getParties()} - 1</tt> indicates the first
+ * to arrive and zero indicates the last to arrive
+ * @throws InterruptedException if the current thread was interrupted
+ * while waiting
+ * @throws TimeoutException if the specified timeout elapses
* @throws BrokenBarrierException if <em>another</em> thread was
- * interrupted while the current thread was waiting, or the barrier was
- * reset, or the barrier was broken when <tt>await</tt> was called,
- * or the barrier action (if present) failed due an exception.
+ * interrupted or timed out while the current thread was
+ * waiting, or the barrier was reset, or the barrier was broken
+ * when {@code await} was called, or the barrier action (if
+ * present) failed due an exception
*/
- public int await(long timeout, TimeUnit unit)
- throws InterruptedException,
- BrokenBarrierException,
- TimeoutException {
+ public int await(long timeout, TimeUnit unit)
+ throws InterruptedException,
+ BrokenBarrierException,
+ TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
/**
* Queries if this barrier is in a broken state.
- * @return <tt>true</tt> if one or more parties broke out of this
- * barrier due to interruption or timeout since construction or
- * the last reset, or a barrier action failed due to an exception;
- * and <tt>false</tt> otherwise.
+ *
+ * @return {@code true} if one or more parties broke out of this
+ * barrier due to interruption or timeout since
+ * construction or the last reset, or a barrier action
+ * failed due to an exception; {@code false} otherwise.
*/
public boolean isBroken() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
- return broken;
+ return generation.broken;
} finally {
lock.unlock();
}
@@ -397,15 +429,8 @@ public class CyclicBarrier {
final ReentrantLock lock = this.lock;
lock.lock();
try {
- /*
- * Retract generation number enough to cover threads
- * currently waiting on current and still resuming from
- * previous generation, plus similarly accommodating spans
- * after the reset.
- */
- generation -= 4;
- broken = false;
- trip.signalAll();
+ breakBarrier(); // break the current generation
+ nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
@@ -416,7 +441,7 @@ public class CyclicBarrier {
* This method is primarily useful for debugging and assertions.
*
* @return the number of parties currently blocked in {@link #await}
- **/
+ */
public int getNumberWaiting() {
final ReentrantLock lock = this.lock;
lock.lock();
@@ -426,5 +451,4 @@ public class CyclicBarrier {
lock.unlock();
}
}
-
}
diff --git a/concurrent/src/main/java/java/util/concurrent/DelayQueue.java b/concurrent/src/main/java/java/util/concurrent/DelayQueue.java
index 156dc7e..52ac4a0 100644
--- a/concurrent/src/main/java/java/util/concurrent/DelayQueue.java
+++ b/concurrent/src/main/java/java/util/concurrent/DelayQueue.java
@@ -14,14 +14,22 @@ import java.util.*;
// END android-note
/**
- * An unbounded {@linkplain BlockingQueue blocking queue} of <tt>Delayed</tt>
- * elements, in which an element can only be taken when its delay has expired.
- * The <em>head</em> of the queue is that <tt>Delayed</tt> element whose delay
- * expired furthest in the past - if no delay has expired there is no head and
- * <tt>poll</tt> will return <tt>null</tt>.
- * This queue does not permit <tt>null</tt> elements.
- * <p>This class implements all of the <em>optional</em> methods
- * of the {@link Collection} and {@link Iterator} interfaces.
+ * An unbounded {@linkplain BlockingQueue blocking queue} of
+ * <tt>Delayed</tt> elements, in which an element can only be taken
+ * when its delay has expired. The <em>head</em> of the queue is that
+ * <tt>Delayed</tt> element whose delay expired furthest in the
+ * past. If no delay has expired there is no head and <tt>poll</tt>
+ * will return <tt>null</tt>. Expiration occurs when an element's
+ * <tt>getDelay(TimeUnit.NANOSECONDS)</tt> method returns a value less
+ * than or equal to zero. Even though unexpired elements cannot be
+ * removed using <tt>take</tt> or <tt>poll</tt>, they are otherwise
+ * treated as normal elements. For example, the <tt>size</tt> method
+ * returns the count of both expired and unexpired elements.
+ * This queue does not permit null elements.
+ *
+ * <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
@@ -32,10 +40,34 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
implements BlockingQueue<E> {
private transient final ReentrantLock lock = new ReentrantLock();
- private transient final Condition available = lock.newCondition();
private final PriorityQueue<E> q = new PriorityQueue<E>();
/**
+ * Thread designated to wait for the element at the head of
+ * the queue. This variant of the Leader-Follower pattern
+ * (http://www.cs.wustl.edu/~schmidt/POSA/POSA2/) serves to
+ * minimize unnecessary timed waiting. When a thread becomes
+ * the leader, it waits only for the next delay to elapse, but
+ * other threads await indefinitely. The leader thread must
+ * signal some other thread before returning from take() or
+ * poll(...), unless some other thread becomes leader in the
+ * interim. Whenever the head of the queue is replaced with
+ * an element with an earlier expiration time, the leader
+ * field is invalidated by being reset to null, and some
+ * waiting thread, but not necessarily the current leader, is
+ * signalled. So waiting threads must be prepared to acquire
+ * and lose leadership while waiting.
+ */
+ private Thread leader = null;
+
+ /**
+ * Condition signalled when a newer element becomes available
+ * at the head of the queue or a new thread may need to
+ * become leader.
+ */
+ private final Condition available = lock.newCondition();
+
+ /**
* Creates a new <tt>DelayQueue</tt> that is initially empty.
*/
public DelayQueue() {}
@@ -44,10 +76,9 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
* Creates a <tt>DelayQueue</tt> initially containing the elements of the
* given collection of {@link Delayed} instances.
*
- * @param c the collection
- * @throws NullPointerException if <tt>c</tt> or any element within it
- * is <tt>null</tt>
- *
+ * @param c the collection of elements to initially contain
+ * @throws NullPointerException if the specified collection or any
+ * of its elements are null
*/
public DelayQueue(Collection<? extends E> c) {
this.addAll(c);
@@ -56,91 +87,136 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
/**
* Inserts the specified element into this delay queue.
*
- * @param o the element to add
+ * @param e the element to add
+ * @return <tt>true</tt> (as specified by {@link Collection#add})
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean add(E e) {
+ return offer(e);
+ }
+
+ /**
+ * Inserts the specified element into this delay queue.
+ *
+ * @param e the element to add
* @return <tt>true</tt>
- * @throws NullPointerException if the specified element is <tt>null</tt>.
+ * @throws NullPointerException if the specified element is null
*/
- public boolean offer(E o) {
+ public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
- E first = q.peek();
- q.offer(o);
- if (first == null || o.compareTo(first) < 0)
- available.signalAll();
+ q.offer(e);
+ if (q.peek() == e) {
+ leader = null;
+ available.signal();
+ }
return true;
} finally {
lock.unlock();
}
}
-
/**
- * Adds the specified element to this delay queue. As the queue is
+ * Inserts the specified element into this delay queue. As the queue is
* unbounded this method will never block.
- * @param o the element to add
- * @throws NullPointerException if the specified element is <tt>null</tt>.
+ *
+ * @param e the element to add
+ * @throws NullPointerException {@inheritDoc}
*/
- public void put(E o) {
- offer(o);
+ public void put(E e) {
+ offer(e);
}
/**
* Inserts the specified element into this delay queue. As the queue is
* unbounded this method will never block.
- * @param o the element to add
+ *
+ * @param e the element to add
* @param timeout This parameter is ignored as the method never blocks
* @param unit This parameter is ignored as the method never blocks
* @return <tt>true</tt>
- * @throws NullPointerException if the specified element is <tt>null</tt>.
+ * @throws NullPointerException {@inheritDoc}
*/
- public boolean offer(E o, long timeout, TimeUnit unit) {
- return offer(o);
+ public boolean offer(E e, long timeout, TimeUnit unit) {
+ return offer(e);
}
/**
- * Adds the specified element to this queue.
- * @param o the element to add
- * @return <tt>true</tt> (as per the general contract of
- * <tt>Collection.add</tt>).
+ * Retrieves and removes the head of this queue, or returns <tt>null</tt>
+ * if this queue has no elements with an expired delay.
*
- * @throws NullPointerException if the specified element is <tt>null</tt>.
+ * @return the head of this queue, or <tt>null</tt> if this
+ * queue has no elements with an expired delay
*/
- public boolean add(E o) {
- return offer(o);
+ public E poll() {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ E first = q.peek();
+ if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
+ return null;
+ else
+ return q.poll();
+ } finally {
+ lock.unlock();
+ }
}
+ /**
+ * Retrieves and removes the head of this queue, waiting if necessary
+ * until an element with an expired delay is available on this queue.
+ *
+ * @return the head of this queue
+ * @throws InterruptedException {@inheritDoc}
+ */
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
E first = q.peek();
- if (first == null) {
+ if (first == null)
available.await();
- } else {
- long delay = first.getDelay(TimeUnit.NANOSECONDS);
- if (delay > 0) {
- long tl = available.awaitNanos(delay);
- } else {
- E x = q.poll();
- assert x != null;
- if (q.size() != 0)
- available.signalAll(); // wake up other takers
- return x;
-
+ else {
+ long delay = first.getDelay(TimeUnit.NANOSECONDS);
+ if (delay <= 0)
+ return q.poll();
+ else if (leader != null)
+ available.await();
+ else {
+ Thread thisThread = Thread.currentThread();
+ leader = thisThread;
+ try {
+ available.awaitNanos(delay);
+ } finally {
+ if (leader == thisThread)
+ leader = null;
+ }
}
}
}
} finally {
+ if (leader == null && q.peek() != null)
+ available.signal();
lock.unlock();
}
}
- public E poll(long time, TimeUnit unit) throws InterruptedException {
+ /**
+ * Retrieves and removes the head of this queue, waiting if necessary
+ * until an element with an expired delay is available on this queue,
+ * or the specified wait time expires.
+ *
+ * @return the head of this queue, or <tt>null</tt> if the
+ * specified waiting time elapses before an element with
+ * an expired delay becomes available
+ * @throws InterruptedException {@inheritDoc}
+ */
+ public E poll(long timeout, TimeUnit unit) throws InterruptedException {
+ long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
- long nanos = unit.toNanos(time);
try {
for (;;) {
E first = q.peek();
@@ -150,46 +226,43 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
else
nanos = available.awaitNanos(nanos);
} else {
- long delay = first.getDelay(TimeUnit.NANOSECONDS);
- if (delay > 0) {
- if (delay > nanos)
- delay = nanos;
- long timeLeft = available.awaitNanos(delay);
- nanos -= delay - timeLeft;
- } else {
- E x = q.poll();
- assert x != null;
- if (q.size() != 0)
- available.signalAll();
- return x;
+ long delay = first.getDelay(TimeUnit.NANOSECONDS);
+ if (delay <= 0)
+ return q.poll();
+ if (nanos <= 0)
+ return null;
+ if (nanos < delay || leader != null)
+ nanos = available.awaitNanos(nanos);
+ else {
+ Thread thisThread = Thread.currentThread();
+ leader = thisThread;
+ try {
+ long timeLeft = available.awaitNanos(delay);
+ nanos -= delay - timeLeft;
+ } finally {
+ if (leader == thisThread)
+ leader = null;
+ }
}
}
}
} finally {
+ if (leader == null && q.peek() != null)
+ available.signal();
lock.unlock();
}
}
-
- public E poll() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- E first = q.peek();
- if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
- return null;
- else {
- E x = q.poll();
- assert x != null;
- if (q.size() != 0)
- available.signalAll();
- return x;
- }
- } finally {
- lock.unlock();
- }
- }
-
+ /**
+ * Retrieves, but does not remove, the head of this queue, or
+ * returns <tt>null</tt> if this queue is empty. Unlike
+ * <tt>poll</tt>, if no expired elements are available in the queue,
+ * this method returns the element that will expire next,
+ * if one exists.
+ *
+ * @return the head of this queue, or <tt>null</tt> if this
+ * queue is empty.
+ */
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
@@ -210,6 +283,12 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
}
}
+ /**
+ * @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();
@@ -226,14 +305,18 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
c.add(q.poll());
++n;
}
- if (n > 0)
- available.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();
@@ -252,8 +335,6 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
c.add(q.poll());
++n;
}
- if (n > 0)
- available.signalAll();
return n;
} finally {
lock.unlock();
@@ -263,6 +344,8 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
/**
* Atomically removes all of the elements from this delay queue.
* The queue will be empty after this call returns.
+ * Elements with an unexpired delay are not waited for; they are
+ * simply discarded from the queue.
*/
public void clear() {
final ReentrantLock lock = this.lock;
@@ -277,12 +360,26 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
/**
* Always returns <tt>Integer.MAX_VALUE</tt> because
* a <tt>DelayQueue</tt> is not capacity constrained.
+ *
* @return <tt>Integer.MAX_VALUE</tt>
*/
public int remainingCapacity() {
return Integer.MAX_VALUE;
}
+ /**
+ * Returns an array containing all of the elements in this queue.
+ * The returned array elements are in no particular order.
+ *
+ * <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 ReentrantLock lock = this.lock;
lock.lock();
@@ -293,16 +390,56 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
}
}
- public <T> T[] toArray(T[] array) {
+ /**
+ * Returns an array containing all of the elements in this queue; the
+ * runtime type of the returned array is that of the specified array.
+ * The returned array elements are in no particular order.
+ * 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>The following code can be used to dump a delay queue into a newly
+ * allocated array of <tt>Delayed</tt>:
+ *
+ * <pre>
+ * Delayed[] a = q.toArray(new Delayed[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 ReentrantLock lock = this.lock;
lock.lock();
try {
- return q.toArray(array);
+ return q.toArray(a);
} finally {
lock.unlock();
}
}
+ /**
+ * Removes a single instance of the specified element from this
+ * queue, if it is present, whether or not it has expired.
+ */
public boolean remove(Object o) {
final ReentrantLock lock = this.lock;
lock.lock();
@@ -314,49 +451,61 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
}
/**
- * Returns an iterator over the elements in this queue. The iterator
- * does not return the elements in any particular order. The
- * returned iterator is a thread-safe "fast-fail" iterator that will
- * throw {@link java.util.ConcurrentModificationException}
- * upon detected interference.
+ * Returns an iterator over all the elements (both expired and
+ * unexpired) in this queue. The iterator does not return the
+ * elements in any particular order. 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.
+ * @return an iterator over the elements in this queue
*/
public Iterator<E> iterator() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return new Itr(q.iterator());
- } finally {
- lock.unlock();
- }
+ return new Itr(toArray());
}
- private class Itr<E> implements Iterator<E> {
- private final Iterator<E> iter;
- Itr(Iterator<E> i) {
- iter = i;
+ /**
+ * Snapshot iterator that works off copy of underlying q array.
+ */
+ private class Itr implements Iterator<E> {
+ final Object[] array; // Array of all elements
+ int cursor; // index of next element to return;
+ int lastRet; // index of last element, or -1 if no such
+
+ Itr(Object[] array) {
+ lastRet = -1;
+ this.array = array;
}
public boolean hasNext() {
- return iter.hasNext();
+ return cursor < array.length;
}
+ @SuppressWarnings("unchecked")
public E next() {
- final ReentrantLock lock = DelayQueue.this.lock;
- lock.lock();
- try {
- return iter.next();
- } finally {
- lock.unlock();
- }
+ if (cursor >= array.length)
+ throw new NoSuchElementException();
+ lastRet = cursor;
+ return (E)array[cursor++];
}
public void remove() {
- final ReentrantLock lock = DelayQueue.this.lock;
+ if (lastRet < 0)
+ throw new IllegalStateException();
+ Object x = array[lastRet];
+ lastRet = -1;
+ // Traverse underlying queue to find == element,
+ // not just a .equals element.
lock.lock();
try {
- iter.remove();
+ for (Iterator it = q.iterator(); it.hasNext(); ) {
+ if (it.next() == x) {
+ it.remove();
+ return;
+ }
+ }
} finally {
lock.unlock();
}
diff --git a/concurrent/src/main/java/java/util/concurrent/Delayed.java b/concurrent/src/main/java/java/util/concurrent/Delayed.java
index bd48292..af41300 100644
--- a/concurrent/src/main/java/java/util/concurrent/Delayed.java
+++ b/concurrent/src/main/java/java/util/concurrent/Delayed.java
@@ -4,6 +4,11 @@
* http://creativecommons.org/licenses/publicdomain
*/
+/*
+ * Modified in Apache Harmony to comply with Java 5 signature
+ * specification.
+ */
+
package java.util.concurrent;
import java.util.*;
@@ -26,11 +31,12 @@ import java.util.*;
public interface Delayed extends Comparable<Delayed> {
/**
- * Returns the delay associated with this object, in the given time unit.
+ * Returns the remaining delay associated with this object, in the
+ * given time unit.
*
* @param unit the time unit
- * @return the delay; zero or negative values indicate that the
- * delay has already elapsed
+ * @return the remaining delay; zero or negative values indicate
+ * that the delay has already elapsed
*/
long getDelay(TimeUnit unit);
}
diff --git a/concurrent/src/main/java/java/util/concurrent/Exchanger.java b/concurrent/src/main/java/java/util/concurrent/Exchanger.java
index da8c815..f67659c 100644
--- a/concurrent/src/main/java/java/util/concurrent/Exchanger.java
+++ b/concurrent/src/main/java/java/util/concurrent/Exchanger.java
@@ -1,27 +1,31 @@
/*
- * Written by Doug Lea with assistance from members of JCP JSR-166
- * Expert Group and released to the public domain, as explained at
+ * Written by Doug Lea, Bill Scherer, and Michael Scott 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.concurrent.atomic.*;
+import java.util.concurrent.locks.LockSupport;
/**
- * A synchronization point at which two threads can exchange objects.
- * Each thread presents some object on entry to the {@link #exchange
- * exchange} method, and receives the object presented by the other
- * thread on return.
+ * A synchronization point at which threads can pair and swap elements
+ * within pairs. Each thread presents some object on entry to the
+ * {@link #exchange exchange} method, matches with a partner thread,
+ * and receives its partner's object on return. An Exchanger may be
+ * viewed as a bidirectional form of a {@link SynchronousQueue}.
+ * Exchangers may be useful in applications such as genetic algorithms
+ * and pipeline designs.
*
* <p><b>Sample Usage:</b>
- * Here are the highlights of a class that uses an <tt>Exchanger</tt> to
- * swap buffers between threads so that the thread filling the
- * buffer gets a freshly
- * emptied one when it needs it, handing off the filled one to
- * the thread emptying the buffer.
- * <pre>
+ * Here are the highlights of a class that uses an {@code Exchanger}
+ * to swap buffers between threads so that the thread filling the
+ * buffer gets a freshly emptied one when it needs it, handing off the
+ * filled one to the thread emptying the buffer.
+ * <pre>{@code
* class FillAndEmpty {
- * Exchanger&lt;DataBuffer&gt; exchanger = new Exchanger();
+ * Exchanger<DataBuffer> exchanger = new Exchanger<DataBuffer>();
* DataBuffer initialEmptyBuffer = ... a made-up type
* DataBuffer initialFullBuffer = ...
*
@@ -31,7 +35,7 @@ import java.util.concurrent.locks.*;
* try {
* while (currentBuffer != null) {
* addToBuffer(currentBuffer);
- * if (currentBuffer.full())
+ * if (currentBuffer.isFull())
* currentBuffer = exchanger.exchange(currentBuffer);
* }
* } catch (InterruptedException ex) { ... handle ... }
@@ -44,7 +48,7 @@ import java.util.concurrent.locks.*;
* try {
* while (currentBuffer != null) {
* takeFromBuffer(currentBuffer);
- * if (currentBuffer.empty())
+ * if (currentBuffer.isEmpty())
* currentBuffer = exchanger.exchange(currentBuffer);
* }
* } catch (InterruptedException ex) { ... handle ...}
@@ -56,192 +60,597 @@ import java.util.concurrent.locks.*;
* new Thread(new EmptyingLoop()).start();
* }
* }
- * </pre>
+ * }</pre>
+ *
+ * <p>Memory consistency effects: For each pair of threads that
+ * successfully exchange objects via an {@code Exchanger}, actions
+ * prior to the {@code exchange()} in each thread
+ * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
+ * those subsequent to a return from the corresponding {@code exchange()}
+ * in the other thread.
*
* @since 1.5
- * @author Doug Lea
+ * @author Doug Lea and Bill Scherer and Michael Scott
* @param <V> The type of objects that may be exchanged
*/
public class Exchanger<V> {
- private final ReentrantLock lock = new ReentrantLock();
- private final Condition taken = lock.newCondition();
+ /*
+ * Algorithm Description:
+ *
+ * The basic idea is to maintain a "slot", which is a reference to
+ * a Node containing both an Item to offer and a "hole" waiting to
+ * get filled in. If an incoming "occupying" thread sees that the
+ * slot is null, it CAS'es (compareAndSets) a Node there and waits
+ * for another to invoke exchange. That second "fulfilling" thread
+ * sees that the slot is non-null, and so CASes it back to null,
+ * also exchanging items by CASing the hole, plus waking up the
+ * occupying thread if it is blocked. In each case CAS'es may
+ * fail because a slot at first appears non-null but is null upon
+ * CAS, or vice-versa. So threads may need to retry these
+ * actions.
+ *
+ * This simple approach works great when there are only a few
+ * threads using an Exchanger, but performance rapidly
+ * deteriorates due to CAS contention on the single slot when
+ * there are lots of threads using an exchanger. So instead we use
+ * an "arena"; basically a kind of hash table with a dynamically
+ * varying number of slots, any one of which can be used by
+ * threads performing an exchange. Incoming threads pick slots
+ * based on a hash of their Thread ids. If an incoming thread
+ * fails to CAS in its chosen slot, it picks an alternative slot
+ * instead. And similarly from there. If a thread successfully
+ * CASes into a slot but no other thread arrives, it tries
+ * another, heading toward the zero slot, which always exists even
+ * if the table shrinks. The particular mechanics controlling this
+ * are as follows:
+ *
+ * Waiting: Slot zero is special in that it is the only slot that
+ * exists when there is no contention. A thread occupying slot
+ * zero will block if no thread fulfills it after a short spin.
+ * In other cases, occupying threads eventually give up and try
+ * another slot. Waiting threads spin for a while (a period that
+ * should be a little less than a typical context-switch time)
+ * before either blocking (if slot zero) or giving up (if other
+ * slots) and restarting. There is no reason for threads to block
+ * unless there are unlikely to be any other threads present.
+ * Occupants are mainly avoiding memory contention so sit there
+ * quietly polling for a shorter period than it would take to
+ * block and then unblock them. Non-slot-zero waits that elapse
+ * because of lack of other threads waste around one extra
+ * context-switch time per try, which is still on average much
+ * faster than alternative approaches.
+ *
+ * Sizing: Usually, using only a few slots suffices to reduce
+ * contention. Especially with small numbers of threads, using
+ * too many slots can lead to just as poor performance as using
+ * too few of them, and there's not much room for error. The
+ * variable "max" maintains the number of slots actually in
+ * use. It is increased when a thread sees too many CAS
+ * failures. (This is analogous to resizing a regular hash table
+ * based on a target load factor, except here, growth steps are
+ * just one-by-one rather than proportional.) Growth requires
+ * contention failures in each of three tried slots. Requiring
+ * multiple failures for expansion copes with the fact that some
+ * failed CASes are not due to contention but instead to simple
+ * races between two threads or thread pre-emptions occurring
+ * between reading and CASing. Also, very transient peak
+ * contention can be much higher than the average sustainable
+ * levels. The max limit is decreased on average 50% of the times
+ * that a non-slot-zero wait elapses without being fulfilled.
+ * Threads experiencing elapsed waits move closer to zero, so
+ * eventually find existing (or future) threads even if the table
+ * has been shrunk due to inactivity. The chosen mechanics and
+ * thresholds for growing and shrinking are intrinsically
+ * entangled with indexing and hashing inside the exchange code,
+ * and can't be nicely abstracted out.
+ *
+ * Hashing: Each thread picks its initial slot to use in accord
+ * with a simple hashcode. The sequence is the same on each
+ * encounter by any given thread, but effectively random across
+ * threads. Using arenas encounters the classic cost vs quality
+ * tradeoffs of all hash tables. Here, we use a one-step FNV-1a
+ * hash code based on the current thread's Thread.getId(), along
+ * with a cheap approximation to a mod operation to select an
+ * index. The downside of optimizing index selection in this way
+ * is that the code is hardwired to use a maximum table size of
+ * 32. But this value more than suffices for known platforms and
+ * applications.
+ *
+ * Probing: On sensed contention of a selected slot, we probe
+ * sequentially through the table, analogously to linear probing
+ * after collision in a hash table. (We move circularly, in
+ * reverse order, to mesh best with table growth and shrinkage
+ * rules.) Except that to minimize the effects of false-alarms
+ * and cache thrashing, we try the first selected slot twice
+ * before moving.
+ *
+ * Padding: Even with contention management, slots are heavily
+ * contended, so use cache-padding to avoid poor memory
+ * performance. Because of this, slots are lazily constructed
+ * only when used, to avoid wasting this space unnecessarily.
+ * While isolation of locations is not much of an issue at first
+ * in an application, as time goes on and garbage-collectors
+ * perform compaction, slots are very likely to be moved adjacent
+ * to each other, which can cause much thrashing of cache lines on
+ * MPs unless padding is employed.
+ *
+ * This is an improvement of the algorithm described in the paper
+ * "A Scalable Elimination-based Exchange Channel" by William
+ * Scherer, Doug Lea, and Michael Scott in Proceedings of SCOOL05
+ * workshop. Available at: http://hdl.handle.net/1802/2104
+ */
+
+ /** The number of CPUs, for sizing and spin control */
+ private static final int NCPU = Runtime.getRuntime().availableProcessors();
+
+ /**
+ * The capacity of the arena. Set to a value that provides more
+ * than enough space to handle contention. On small machines
+ * most slots won't be used, but it is still not wasted because
+ * the extra space provides some machine-level address padding
+ * to minimize interference with heavily CAS'ed Slot locations.
+ * And on very large machines, performance eventually becomes
+ * bounded by memory bandwidth, not numbers of threads/CPUs.
+ * This constant cannot be changed without also modifying
+ * indexing and hashing algorithms.
+ */
+ private static final int CAPACITY = 32;
- /** Holder for the item being exchanged */
- private V item;
-
/**
- * Arrival count transitions from 0 to 1 to 2 then back to 0
- * during an exchange.
+ * The value of "max" that will hold all threads without
+ * contention. When this value is less than CAPACITY, some
+ * otherwise wasted expansion can be avoided.
*/
- private int arrivalCount;
+ private static final int FULL =
+ Math.max(0, Math.min(CAPACITY, NCPU / 2) - 1);
+
+ /**
+ * The number of times to spin (doing nothing except polling a
+ * memory location) before blocking or giving up while waiting to
+ * be fulfilled. Should be zero on uniprocessors. On
+ * multiprocessors, this value should be large enough so that two
+ * threads exchanging items as fast as possible block only when
+ * one of them is stalled (due to GC or preemption), but not much
+ * longer, to avoid wasting CPU resources. Seen differently, this
+ * value is a little over half the number of cycles of an average
+ * context switch time on most systems. The value here is
+ * approximately the average of those across a range of tested
+ * systems.
+ */
+ private static final int SPINS = (NCPU == 1) ? 0 : 2000;
+
+ /**
+ * The number of times to spin before blocking in timed waits.
+ * Timed waits spin more slowly because checking the time takes
+ * time. The best value relies mainly on the relative rate of
+ * System.nanoTime vs memory accesses. The value is empirically
+ * derived to work well across a variety of systems.
+ */
+ private static final int TIMED_SPINS = SPINS / 20;
+
+ /**
+ * Sentinel item representing cancellation of a wait due to
+ * interruption, timeout, or elapsed spin-waits. This value is
+ * placed in holes on cancellation, and used as a return value
+ * from waiting methods to indicate failure to set or get hole.
+ */
+ private static final Object CANCEL = new Object();
+
+ /**
+ * Value representing null arguments/returns from public
+ * methods. This disambiguates from internal requirement that
+ * holes start out as null to mean they are not yet set.
+ */
+ private static final Object NULL_ITEM = new Object();
+
+ /**
+ * Nodes hold partially exchanged data. This class
+ * opportunistically subclasses AtomicReference to represent the
+ * hole. So get() returns hole, and compareAndSet CAS'es value
+ * into hole. This class cannot be parameterized as "V" because
+ * of the use of non-V CANCEL sentinels.
+ */
+ private static final class Node extends AtomicReference<Object> {
+ /** The element offered by the Thread creating this node. */
+ public final Object item;
+
+ /** The Thread waiting to be signalled; null until waiting. */
+ public volatile Thread waiter;
+
+ /**
+ * Creates node with given item and empty hole.
+ * @param item the item
+ */
+ public Node(Object item) {
+ this.item = item;
+ }
+ }
+
+ /**
+ * A Slot is an AtomicReference with heuristic padding to lessen
+ * cache effects of this heavily CAS'ed location. While the
+ * padding adds noticeable space, all slots are created only on
+ * demand, and there will be more than one of them only when it
+ * would improve throughput more than enough to outweigh using
+ * extra space.
+ */
+ private static final class Slot extends AtomicReference<Object> {
+ // Improve likelihood of isolation on <= 64 byte cache lines
+ long q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, qa, qb, qc, qd, qe;
+ }
+
+ /**
+ * Slot array. Elements are lazily initialized when needed.
+ * Declared volatile to enable double-checked lazy construction.
+ */
+ private volatile Slot[] arena = new Slot[CAPACITY];
+
+ /**
+ * The maximum slot index being used. The value sometimes
+ * increases when a thread experiences too many CAS contentions,
+ * and sometimes decreases when a spin-wait elapses. Changes
+ * are performed only via compareAndSet, to avoid stale values
+ * when a thread happens to stall right before setting.
+ */
+ private final AtomicInteger max = new AtomicInteger();
/**
* Main exchange function, handling the different policy variants.
+ * Uses Object, not "V" as argument and return value to simplify
+ * handling of sentinel values. Callers from public methods decode
+ * and cast accordingly.
+ *
+ * @param item the (non-null) item to exchange
+ * @param timed true if the wait is timed
+ * @param nanos if timed, the maximum wait time
+ * @return the other thread's item, or CANCEL if interrupted or timed out
*/
- private V doExchange(V x, boolean timed, long nanos) throws InterruptedException, TimeoutException {
- lock.lock();
- try {
- V other;
-
- // If arrival count already at two, we must wait for
- // a previous pair to finish and reset the count;
- while (arrivalCount == 2) {
- if (!timed)
- taken.await();
- else if (nanos > 0)
- nanos = taken.awaitNanos(nanos);
- else
- throw new TimeoutException();
+ private Object doExchange(Object item, boolean timed, long nanos) {
+ Node me = new Node(item); // Create in case occupying
+ int index = hashIndex(); // Index of current slot
+ int fails = 0; // Number of CAS failures
+
+ for (;;) {
+ Object y; // Contents of current slot
+ Slot slot = arena[index];
+ if (slot == null) // Lazily initialize slots
+ createSlot(index); // Continue loop to reread
+ else if ((y = slot.get()) != null && // Try to fulfill
+ slot.compareAndSet(y, null)) {
+ Node you = (Node)y; // Transfer item
+ if (you.compareAndSet(null, item)) {
+ LockSupport.unpark(you.waiter);
+ return you.item;
+ } // Else cancelled; continue
+ }
+ else if (y == null && // Try to occupy
+ slot.compareAndSet(null, me)) {
+ if (index == 0) // Blocking wait for slot 0
+ return timed? awaitNanos(me, slot, nanos): await(me, slot);
+ Object v = spinWait(me, slot); // Spin wait for non-0
+ if (v != CANCEL)
+ return v;
+ me = new Node(item); // Throw away cancelled node
+ int m = max.get();
+ if (m > (index >>>= 1)) // Decrease index
+ max.compareAndSet(m, m - 1); // Maybe shrink table
+ }
+ else if (++fails > 1) { // Allow 2 fails on 1st slot
+ int m = max.get();
+ if (fails > 3 && m < FULL && max.compareAndSet(m, m + 1))
+ index = m + 1; // Grow on 3rd failed slot
+ else if (--index < 0)
+ index = m; // Circularly traverse
}
+ }
+ }
- int count = ++arrivalCount;
+ /**
+ * Returns a hash index for the current thread. Uses a one-step
+ * FNV-1a hash code (http://www.isthe.com/chongo/tech/comp/fnv/)
+ * based on the current thread's Thread.getId(). These hash codes
+ * have more uniform distribution properties with respect to small
+ * moduli (here 1-31) than do other simple hashing functions.
+ *
+ * <p>To return an index between 0 and max, we use a cheap
+ * approximation to a mod operation, that also corrects for bias
+ * due to non-power-of-2 remaindering (see {@link
+ * java.util.Random#nextInt}). Bits of the hashcode are masked
+ * with "nbits", the ceiling power of two of table size (looked up
+ * in a table packed into three ints). If too large, this is
+ * retried after rotating the hash by nbits bits, while forcing new
+ * top bit to 0, which guarantees eventual termination (although
+ * with a non-random-bias). This requires an average of less than
+ * 2 tries for all table sizes, and has a maximum 2% difference
+ * from perfectly uniform slot probabilities when applied to all
+ * possible hash codes for sizes less than 32.
+ *
+ * @return a per-thread-random index, 0 <= index < max
+ */
+ private final int hashIndex() {
+ long id = Thread.currentThread().getId();
+ int hash = (((int)(id ^ (id >>> 32))) ^ 0x811c9dc5) * 0x01000193;
- // If item is already waiting, replace it and signal other thread
- if (count == 2) {
- other = item;
- item = x;
- taken.signal();
- return other;
- }
+ int m = max.get();
+ int nbits = (((0xfffffc00 >> m) & 4) | // Compute ceil(log2(m+1))
+ ((0x000001f8 >>> m) & 2) | // The constants hold
+ ((0xffff00f2 >>> m) & 1)); // a lookup table
+ int index;
+ while ((index = hash & ((1 << nbits) - 1)) > m) // May retry on
+ hash = (hash >>> nbits) | (hash << (33 - nbits)); // non-power-2 m
+ return index;
+ }
- // Otherwise, set item and wait for another thread to
- // replace it and signal us.
-
- item = x;
- InterruptedException interrupted = null;
- try {
- while (arrivalCount != 2) {
- if (!timed)
- taken.await();
- else if (nanos > 0)
- nanos = taken.awaitNanos(nanos);
- else
- break; // timed out
- }
- } catch (InterruptedException ie) {
- interrupted = ie;
- }
+ /**
+ * Creates a new slot at given index. Called only when the slot
+ * appears to be null. Relies on double-check using builtin
+ * locks, since they rarely contend. This in turn relies on the
+ * arena array being declared volatile.
+ *
+ * @param index the index to add slot at
+ */
+ private void createSlot(int index) {
+ // Create slot outside of lock to narrow sync region
+ Slot newSlot = new Slot();
+ Slot[] a = arena;
+ synchronized (a) {
+ if (a[index] == null)
+ a[index] = newSlot;
+ }
+ }
+
+ /**
+ * Tries to cancel a wait for the given node waiting in the given
+ * slot, if so, helping clear the node from its slot to avoid
+ * garbage retention.
+ *
+ * @param node the waiting node
+ * @param slot the slot it is waiting in
+ * @return true if successfully cancelled
+ */
+ private static boolean tryCancel(Node node, Slot slot) {
+ if (!node.compareAndSet(null, CANCEL))
+ return false;
+ if (slot.get() == node) // pre-check to minimize contention
+ slot.compareAndSet(node, null);
+ return true;
+ }
+
+ // Three forms of waiting. Each just different enough not to merge
+ // code with others.
+
+ /**
+ * Spin-waits for hole for a non-0 slot. Fails if spin elapses
+ * before hole filled. Does not check interrupt, relying on check
+ * in public exchange method to abort if interrupted on entry.
+ *
+ * @param node the waiting node
+ * @return on success, the hole; on failure, CANCEL
+ */
+ private static Object spinWait(Node node, Slot slot) {
+ int spins = SPINS;
+ for (;;) {
+ Object v = node.get();
+ if (v != null)
+ return v;
+ else if (spins > 0)
+ --spins;
+ else
+ tryCancel(node, slot);
+ }
+ }
+
+ /**
+ * Waits for (by spinning and/or blocking) and gets the hole
+ * filled in by another thread. Fails if interrupted before
+ * hole filled.
+ *
+ * When a node/thread is about to block, it sets its waiter field
+ * and then rechecks state at least one more time before actually
+ * parking, thus covering race vs fulfiller noticing that waiter
+ * is non-null so should be woken.
+ *
+ * Thread interruption status is checked only surrounding calls to
+ * park. The caller is assumed to have checked interrupt status
+ * on entry.
+ *
+ * @param node the waiting node
+ * @return on success, the hole; on failure, CANCEL
+ */
+ private static Object await(Node node, Slot slot) {
+ Thread w = Thread.currentThread();
+ int spins = SPINS;
+ for (;;) {
+ Object v = node.get();
+ if (v != null)
+ return v;
+ else if (spins > 0) // Spin-wait phase
+ --spins;
+ else if (node.waiter == null) // Set up to block next
+ node.waiter = w;
+ else if (w.isInterrupted()) // Abort on interrupt
+ tryCancel(node, slot);
+ else // Block
+ LockSupport.park();
+ }
+ }
- // Get and reset item and count after the wait.
- // (We need to do this even if wait was aborted.)
- other = item;
- item = null;
- count = arrivalCount;
- arrivalCount = 0;
- taken.signal();
-
- // If the other thread replaced item, then we must
- // continue even if cancelled.
- if (count == 2) {
- if (interrupted != null)
- Thread.currentThread().interrupt();
- return other;
+ /**
+ * Waits for (at index 0) and gets the hole filled in by another
+ * thread. Fails if timed out or interrupted before hole filled.
+ * Same basic logic as untimed version, but a bit messier.
+ *
+ * @param node the waiting node
+ * @param nanos the wait time
+ * @return on success, the hole; on failure, CANCEL
+ */
+ private Object awaitNanos(Node node, Slot slot, long nanos) {
+ int spins = TIMED_SPINS;
+ long lastTime = 0;
+ Thread w = null;
+ for (;;) {
+ Object v = node.get();
+ if (v != null)
+ return v;
+ long now = System.nanoTime();
+ if (w == null)
+ w = Thread.currentThread();
+ else
+ nanos -= now - lastTime;
+ lastTime = now;
+ if (nanos > 0) {
+ if (spins > 0)
+ --spins;
+ else if (node.waiter == null)
+ node.waiter = w;
+ else if (w.isInterrupted())
+ tryCancel(node, slot);
+ else
+ LockSupport.parkNanos(nanos);
}
+ else if (tryCancel(node, slot) && !w.isInterrupted())
+ return scanOnTimeout(node);
+ }
+ }
- // If no one is waiting for us, we can back out
- if (interrupted != null)
- throw interrupted;
- else // must be timeout
- throw new TimeoutException();
- } finally {
- lock.unlock();
+ /**
+ * Sweeps through arena checking for any waiting threads. Called
+ * only upon return from timeout while waiting in slot 0. When a
+ * thread gives up on a timed wait, it is possible that a
+ * previously-entered thread is still waiting in some other
+ * slot. So we scan to check for any. This is almost always
+ * overkill, but decreases the likelihood of timeouts when there
+ * are other threads present to far less than that in lock-based
+ * exchangers in which earlier-arriving threads may still be
+ * waiting on entry locks.
+ *
+ * @param node the waiting node
+ * @return another thread's item, or CANCEL
+ */
+ private Object scanOnTimeout(Node node) {
+ Object y;
+ for (int j = arena.length - 1; j >= 0; --j) {
+ Slot slot = arena[j];
+ if (slot != null) {
+ while ((y = slot.get()) != null) {
+ if (slot.compareAndSet(y, null)) {
+ Node you = (Node)y;
+ if (you.compareAndSet(null, node.item)) {
+ LockSupport.unpark(you.waiter);
+ return you.item;
+ }
+ }
+ }
+ }
}
+ return CANCEL;
}
/**
- * Create a new Exchanger.
- **/
+ * Creates a new Exchanger.
+ */
public Exchanger() {
}
/**
* Waits for another thread to arrive at this exchange point (unless
- * it is {@link Thread#interrupt interrupted}),
+ * the current thread is {@linkplain Thread#interrupt interrupted}),
* and then transfers the given object to it, receiving its object
* in return.
+ *
* <p>If another thread is already waiting at the exchange point then
* it is resumed for thread scheduling purposes and receives the object
- * passed in by the current thread. The current thread returns immediately,
+ * passed in by the current thread. The current thread returns immediately,
* receiving the object passed to the exchange by that other thread.
- * <p>If no other thread is already waiting at the exchange then the
+ *
+ * <p>If no other thread is already waiting at the exchange then the
* current thread is disabled for thread scheduling purposes and lies
* dormant until one of two things happens:
* <ul>
* <li>Some other thread enters the exchange; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts} the current
* thread.
* </ul>
* <p>If the current thread:
* <ul>
- * <li>has its interrupted status set on entry to this method; or
- * <li>is {@link Thread#interrupt interrupted} while waiting
- * for the exchange,
+ * <li>has its interrupted status set on entry to this method; or
+ * <li>is {@linkplain Thread#interrupt interrupted} while waiting
+ * for the exchange,
* </ul>
- * then {@link InterruptedException} is thrown and the current thread's
- * interrupted status is cleared.
+ * then {@link InterruptedException} is thrown and the current thread's
+ * interrupted status is cleared.
*
* @param x the object to exchange
- * @return the object provided by the other thread.
- * @throws InterruptedException if current thread was interrupted
- * while waiting
- **/
+ * @return the object provided by the other thread
+ * @throws InterruptedException if the current thread was
+ * interrupted while waiting
+ */
public V exchange(V x) throws InterruptedException {
- try {
- return doExchange(x, false, 0);
- } catch (TimeoutException cannotHappen) {
- throw new Error(cannotHappen);
+ if (!Thread.interrupted()) {
+ Object v = doExchange(x == null? NULL_ITEM : x, false, 0);
+ if (v == NULL_ITEM)
+ return null;
+ if (v != CANCEL)
+ return (V)v;
+ Thread.interrupted(); // Clear interrupt status on IE throw
}
+ throw new InterruptedException();
}
/**
* Waits for another thread to arrive at this exchange point (unless
- * it is {@link Thread#interrupt interrupted}, or the specified waiting
- * time elapses),
- * and then transfers the given object to it, receiving its object
- * in return.
+ * the current thread is {@linkplain Thread#interrupt interrupted} or
+ * the specified waiting time elapses), and then transfers the given
+ * object to it, receiving its object in return.
*
* <p>If another thread is already waiting at the exchange point then
* it is resumed for thread scheduling purposes and receives the object
- * passed in by the current thread. The current thread returns immediately,
+ * passed in by the current thread. The current thread returns immediately,
* receiving the object passed to the exchange by that other thread.
*
- * <p>If no other thread is already waiting at the exchange then the
+ * <p>If no other thread is already waiting at the exchange then the
* current thread is disabled for thread scheduling purposes and lies
* dormant until one of three things happens:
* <ul>
* <li>Some other thread enters the exchange; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread; or
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
+ * the current thread; or
* <li>The specified waiting time elapses.
* </ul>
* <p>If the current thread:
* <ul>
- * <li>has its interrupted status set on entry to this method; or
- * <li>is {@link Thread#interrupt interrupted} while waiting
- * for the exchange,
+ * <li>has its interrupted status set on entry to this method; or
+ * <li>is {@linkplain Thread#interrupt interrupted} while waiting
+ * for the exchange,
* </ul>
- * then {@link InterruptedException} is thrown and the current thread's
- * interrupted status is cleared.
+ * then {@link InterruptedException} is thrown and the current thread's
+ * interrupted status is cleared.
*
- * <p>If the specified waiting time elapses then {@link TimeoutException}
- * is thrown.
- * If the time is
- * less than or equal to zero, the method will not wait at all.
+ * <p>If the specified waiting time elapses then {@link
+ * TimeoutException} is thrown. If the time is less than or equal
+ * to zero, the method will not wait at all.
*
* @param x the object to exchange
* @param timeout the maximum time to wait
- * @param unit the time unit of the <tt>timeout</tt> argument.
- * @return the object provided by the other thread.
- * @throws InterruptedException if current thread was interrupted
- * while waiting
- * @throws TimeoutException if the specified waiting time elapses before
- * another thread enters the exchange.
- **/
- public V exchange(V x, long timeout, TimeUnit unit)
+ * @param unit the time unit of the <tt>timeout</tt> argument
+ * @return the object provided by the other thread
+ * @throws InterruptedException if the current thread was
+ * interrupted while waiting
+ * @throws TimeoutException if the specified waiting time elapses
+ * before another thread enters the exchange
+ */
+ public V exchange(V x, long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException {
- return doExchange(x, true, unit.toNanos(timeout));
+ if (!Thread.interrupted()) {
+ Object v = doExchange(x == null? NULL_ITEM : x,
+ true, unit.toNanos(timeout));
+ if (v == NULL_ITEM)
+ return null;
+ if (v != CANCEL)
+ return (V)v;
+ if (!Thread.interrupted())
+ throw new TimeoutException();
+ }
+ throw new InterruptedException();
}
-
}
-
-
diff --git a/concurrent/src/main/java/java/util/concurrent/ExecutionException.java b/concurrent/src/main/java/java/util/concurrent/ExecutionException.java
index 8f0e448..bc561e5 100644
--- a/concurrent/src/main/java/java/util/concurrent/ExecutionException.java
+++ b/concurrent/src/main/java/java/util/concurrent/ExecutionException.java
@@ -19,14 +19,14 @@ public class ExecutionException extends Exception {
private static final long serialVersionUID = 7830266012832686185L;
/**
- * Constructs a <tt>ExecutionException</tt> with no detail message.
+ * Constructs an <tt>ExecutionException</tt> with no detail message.
* The cause is not initialized, and may subsequently be
* initialized by a call to {@link #initCause(Throwable) initCause}.
*/
protected ExecutionException() { }
/**
- * Constructs a <tt>ExecutionException</tt> with the specified detail
+ * Constructs an <tt>ExecutionException</tt> with the specified detail
* message. The cause is not initialized, and may subsequently be
* initialized by a call to {@link #initCause(Throwable) initCause}.
*
@@ -37,7 +37,7 @@ public class ExecutionException extends Exception {
}
/**
- * Constructs a <tt>ExecutionException</tt> with the specified detail
+ * Constructs an <tt>ExecutionException</tt> with the specified detail
* message and cause.
*
* @param message the detail message
@@ -49,7 +49,7 @@ public class ExecutionException extends Exception {
}
/**
- * Constructs a <tt>ExecutionException</tt> with the specified cause.
+ * Constructs an <tt>ExecutionException</tt> with the specified cause.
* The detail message is set to:
* <pre>
* (cause == null ? null : cause.toString())</pre>
diff --git a/concurrent/src/main/java/java/util/concurrent/Executor.java b/concurrent/src/main/java/java/util/concurrent/Executor.java
index 60fe193..a61e921 100644
--- a/concurrent/src/main/java/java/util/concurrent/Executor.java
+++ b/concurrent/src/main/java/java/util/concurrent/Executor.java
@@ -21,7 +21,7 @@ package java.util.concurrent;
* executor.execute(new RunnableTask2());
* ...
* </pre>
- *
+ *
* However, the <tt>Executor</tt> interface does not strictly
* require that execution be asynchronous. In the simplest case, an
* executor can run the submitted task immediately in the caller's
@@ -52,7 +52,7 @@ package java.util.concurrent;
*
* <pre>
* class SerialExecutor implements Executor {
- * final Queue&lt;Runnable&gt; tasks = new LinkedBlockingQueue&lt;Runnable&gt;();
+ * final Queue&lt;Runnable&gt; tasks = new ArrayDeque&lt;Runnable&gt;();
* final Executor executor;
* Runnable active;
*
@@ -88,6 +88,11 @@ package java.util.concurrent;
* extensible thread pool implementation. The {@link Executors} class
* provides convenient factory methods for these Executors.
*
+ * <p>Memory consistency effects: Actions in a thread prior to
+ * submitting a {@code Runnable} object to an {@code Executor}
+ * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
+ * its execution begins, perhaps in another thread.
+ *
* @since 1.5
* @author Doug Lea
*/
diff --git a/concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java b/concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java
index 4f9c9d9..02606f9 100644
--- a/concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java
+++ b/concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java
@@ -6,11 +6,10 @@
package java.util.concurrent;
-
/**
* A {@link CompletionService} that uses a supplied {@link Executor}
* to execute tasks. This class arranges that submitted tasks are,
- * upon completion, placed on a queue accessible using <tt>take</tt>.
+ * upon completion, placed on a queue accessible using {@code take}.
* The class is lightweight enough to be suitable for transient use
* when processing groups of tasks.
*
@@ -19,84 +18,102 @@ package java.util.concurrent;
* <b>Usage Examples.</b>
*
* Suppose you have a set of solvers for a certain problem, each
- * returning a value of some type <tt>Result</tt>, and would like to
+ * returning a value of some type {@code Result}, and would like to
* run them concurrently, processing the results of each of them that
- * return a non-null value, in some method <tt>use(Result r)</tt>. You
+ * return a non-null value, in some method {@code use(Result r)}. You
* could write this as:
*
- * <pre>
- * void solve(Executor e, Collection&lt;Callable&lt;Result&gt;&gt; solvers)
- * throws InterruptedException, ExecutionException {
- * CompletionService&lt;Result&gt; ecs = new ExecutorCompletionService&lt;Result&gt;(e);
- * for (Callable&lt;Result&gt; s : solvers)
- * ecs.submit(s);
- * int n = solvers.size();
- * for (int i = 0; i &lt; n; ++i) {
- * Result r = ecs.take().get();
- * if (r != null)
- * use(r);
- * }
- * }
- * </pre>
+ * <pre> {@code
+ * void solve(Executor e,
+ * Collection<Callable<Result>> solvers)
+ * throws InterruptedException, ExecutionException {
+ * CompletionService<Result> ecs
+ * = new ExecutorCompletionService<Result>(e);
+ * for (Callable<Result> s : solvers)
+ * ecs.submit(s);
+ * int n = solvers.size();
+ * for (int i = 0; i < n; ++i) {
+ * Result r = ecs.take().get();
+ * if (r != null)
+ * use(r);
+ * }
+ * }}</pre>
*
* Suppose instead that you would like to use the first non-null result
* of the set of tasks, ignoring any that encounter exceptions,
* and cancelling all other tasks when the first one is ready:
*
- * <pre>
- * void solve(Executor e, Collection&lt;Callable&lt;Result&gt;&gt; solvers)
- * throws InterruptedException {
- * CompletionService&lt;Result&gt; ecs = new ExecutorCompletionService&lt;Result&gt;(e);
- * int n = solvers.size();
- * List&lt;Future&lt;Result&gt;&gt; futures = new ArrayList&lt;Future&lt;Result&gt;&gt;(n);
- * Result result = null;
- * try {
- * for (Callable&lt;Result&gt; s : solvers)
- * futures.add(ecs.submit(s));
- * for (int i = 0; i &lt; n; ++i) {
- * try {
- * Result r = ecs.take().get();
- * if (r != null) {
- * result = r;
- * break;
- * }
- * } catch(ExecutionException ignore) {}
- * }
- * }
- * finally {
- * for (Future&lt;Result&gt; f : futures)
- * f.cancel(true);
- * }
+ * <pre> {@code
+ * void solve(Executor e,
+ * Collection<Callable<Result>> solvers)
+ * throws InterruptedException {
+ * CompletionService<Result> ecs
+ * = new ExecutorCompletionService<Result>(e);
+ * int n = solvers.size();
+ * List<Future<Result>> futures
+ * = new ArrayList<Future<Result>>(n);
+ * Result result = null;
+ * try {
+ * for (Callable<Result> s : solvers)
+ * futures.add(ecs.submit(s));
+ * for (int i = 0; i < n; ++i) {
+ * try {
+ * Result r = ecs.take().get();
+ * if (r != null) {
+ * result = r;
+ * break;
+ * }
+ * } catch (ExecutionException ignore) {}
+ * }
+ * }
+ * finally {
+ * for (Future<Result> f : futures)
+ * f.cancel(true);
+ * }
*
- * if (result != null)
- * use(result);
- * }
- * </pre>
+ * if (result != null)
+ * use(result);
+ * }}</pre>
*/
public class ExecutorCompletionService<V> implements CompletionService<V> {
private final Executor executor;
+ private final AbstractExecutorService aes;
private final BlockingQueue<Future<V>> completionQueue;
/**
* FutureTask extension to enqueue upon completion
*/
- private class QueueingFuture extends FutureTask<V> {
- QueueingFuture(Callable<V> c) { super(c); }
- QueueingFuture(Runnable t, V r) { super(t, r); }
- protected void done() { completionQueue.add(this); }
+ private class QueueingFuture extends FutureTask<Void> {
+ QueueingFuture(FutureTask<V> task) {
+ super(task, null);
+ this.task = task;
+ }
+ protected void done() { completionQueue.add(task); }
+ private final Future<V> task;
+ }
+
+ private FutureTask<V> newTaskFor(Callable<V> task) {
+ return new FutureTask<V>(task);
+ }
+
+ private FutureTask<V> newTaskFor(Runnable task, V result) {
+ return new FutureTask<V>(task, result);
}
/**
* Creates an ExecutorCompletionService using the supplied
* executor for base task execution and a
* {@link LinkedBlockingQueue} as a completion queue.
+ *
* @param executor the executor to use
- * @throws NullPointerException if executor is <tt>null</tt>
+ * @throws NullPointerException if executor is {@code null}
*/
public ExecutorCompletionService(Executor executor) {
- if (executor == null)
+ if (executor == null)
throw new NullPointerException();
this.executor = executor;
+ this.aes = (executor instanceof AbstractExecutorService) ?
+ (AbstractExecutorService) executor : null;
this.completionQueue = new LinkedBlockingQueue<Future<V>>();
}
@@ -104,30 +121,36 @@ public class ExecutorCompletionService<V> implements CompletionService<V> {
* Creates an ExecutorCompletionService using the supplied
* executor for base task execution and the supplied queue as its
* completion queue.
+ *
* @param executor the executor to use
* @param completionQueue the queue to use as the completion queue
- * normally one dedicated for use by this service
- * @throws NullPointerException if executor or completionQueue are <tt>null</tt>
+ * normally one dedicated for use by this service. This
+ * queue is treated as unbounded -- failed attempted
+ * {@code Queue.add} operations for completed taskes cause
+ * them not to be retrievable.
+ * @throws NullPointerException if executor or completionQueue are {@code null}
*/
public ExecutorCompletionService(Executor executor,
BlockingQueue<Future<V>> completionQueue) {
- if (executor == null || completionQueue == null)
+ if (executor == null || completionQueue == null)
throw new NullPointerException();
this.executor = executor;
+ this.aes = (executor instanceof AbstractExecutorService) ?
+ (AbstractExecutorService) executor : null;
this.completionQueue = completionQueue;
}
public Future<V> submit(Callable<V> task) {
if (task == null) throw new NullPointerException();
- QueueingFuture f = new QueueingFuture(task);
- executor.execute(f);
+ FutureTask<V> f = newTaskFor(task);
+ executor.execute(new QueueingFuture(f));
return f;
}
public Future<V> submit(Runnable task, V result) {
if (task == null) throw new NullPointerException();
- QueueingFuture f = new QueueingFuture(task, result);
- executor.execute(f);
+ FutureTask<V> f = newTaskFor(task, result);
+ executor.execute(new QueueingFuture(f));
return f;
}
@@ -144,5 +167,3 @@ public class ExecutorCompletionService<V> implements CompletionService<V> {
}
}
-
-
diff --git a/concurrent/src/main/java/java/util/concurrent/ExecutorService.java b/concurrent/src/main/java/java/util/concurrent/ExecutorService.java
index e178eb2..757f7cb 100644
--- a/concurrent/src/main/java/java/util/concurrent/ExecutorService.java
+++ b/concurrent/src/main/java/java/util/concurrent/ExecutorService.java
@@ -5,7 +5,6 @@
*/
package java.util.concurrent;
-
import java.util.List;
import java.util.Collection;
import java.security.PrivilegedAction;
@@ -14,14 +13,18 @@ import java.security.PrivilegedExceptionAction;
/**
* An {@link Executor} that provides methods to manage termination and
* methods that can produce a {@link Future} for tracking progress of
- * one or more asynchronous tasks.
+ * one or more asynchronous tasks.
*
- * <p>
- * An <tt>ExecutorService</tt> can be shut down, which will cause it
- * to stop accepting new tasks. After being shut down, the executor
- * will eventually terminate, at which point no tasks are actively
- * executing, no tasks are awaiting execution, and no new tasks can be
- * submitted.
+ * <p> An <tt>ExecutorService</tt> can be shut down, which will cause
+ * it to reject new tasks. Two different methods are provided for
+ * shutting down an <tt>ExecutorService</tt>. The {@link #shutdown}
+ * method will allow previously submitted tasks to execute before
+ * terminating, while the {@link #shutdownNow} method prevents waiting
+ * tasks from starting and attempts to stop currently executing tasks.
+ * Upon termination, an executor has no tasks actively executing, no
+ * tasks awaiting execution, and no new tasks can be submitted. An
+ * unused <tt>ExecutorService</tt> should be shut down to allow
+ * reclamation of its resources.
*
* <p> Method <tt>submit</tt> extends base method {@link
* Executor#execute} by creating and returning a {@link Future} that
@@ -35,41 +38,74 @@ import java.security.PrivilegedExceptionAction;
* <p>The {@link Executors} class provides factory methods for the
* executor services provided in this package.
*
- * <h3>Usage Example</h3>
+ * <h3>Usage Examples</h3>
*
* Here is a sketch of a network service in which threads in a thread
* pool service incoming requests. It uses the preconfigured {@link
* Executors#newFixedThreadPool} factory method:
*
* <pre>
- * class NetworkService {
- * private final ServerSocket serverSocket;
- * private final ExecutorService pool;
+ * class NetworkService implements Runnable {
+ * private final ServerSocket serverSocket;
+ * private final ExecutorService pool;
+ *
+ * public NetworkService(int port, int poolSize)
+ * throws IOException {
+ * serverSocket = new ServerSocket(port);
+ * pool = Executors.newFixedThreadPool(poolSize);
+ * }
+ *
+ * public void run() { // run the service
+ * try {
+ * for (;;) {
+ * pool.execute(new Handler(serverSocket.accept()));
+ * }
+ * } catch (IOException ex) {
+ * pool.shutdown();
+ * }
+ * }
+ * }
+ *
+ * class Handler implements Runnable {
+ * private final Socket socket;
+ * Handler(Socket socket) { this.socket = socket; }
+ * public void run() {
+ * // read and service request on socket
+ * }
+ * }
+ * </pre>
*
- * public NetworkService(int port, int poolSize) throws IOException {
- * serverSocket = new ServerSocket(port);
- * pool = Executors.newFixedThreadPool(poolSize);
- * }
- *
- * public void serve() {
- * try {
- * for (;;) {
- * pool.execute(new Handler(serverSocket.accept()));
- * }
- * } catch (IOException ex) {
- * pool.shutdown();
- * }
- * }
- * }
+ * The following method shuts down an <tt>ExecutorService</tt> in two phases,
+ * first by calling <tt>shutdown</tt> to reject incoming tasks, and then
+ * calling <tt>shutdownNow</tt>, if necessary, to cancel any lingering tasks:
*
- * class Handler implements Runnable {
- * private final Socket socket;
- * Handler(Socket socket) { this.socket = socket; }
- * public void run() {
- * // read and service request
- * }
+ * <pre>
+ * void shutdownAndAwaitTermination(ExecutorService pool) {
+ * pool.shutdown(); // Disable new tasks from being submitted
+ * try {
+ * // Wait a while for existing tasks to terminate
+ * if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
+ * pool.shutdownNow(); // Cancel currently executing tasks
+ * // Wait a while for tasks to respond to being cancelled
+ * if (!pool.awaitTermination(60, TimeUnit.SECONDS))
+ * System.err.println("Pool did not terminate");
+ * }
+ * } catch (InterruptedException ie) {
+ * // (Re-)Cancel if current thread also interrupted
+ * pool.shutdownNow();
+ * // Preserve interrupt status
+ * Thread.currentThread().interrupt();
+ * }
* }
* </pre>
+ *
+ * <p>Memory consistency effects: Actions in a thread prior to the
+ * submission of a {@code Runnable} or {@code Callable} task to an
+ * {@code ExecutorService}
+ * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
+ * any actions taken by that task, which in turn <i>happen-before</i> the
+ * result is retrieved via {@code Future.get()}.
+ *
* @since 1.5
* @author Doug Lea
*/
@@ -77,33 +113,45 @@ public interface ExecutorService extends Executor {
/**
* Initiates an orderly shutdown in which previously submitted
- * tasks are executed, but no new tasks will be
- * accepted. Invocation has no additional effect if already shut
- * down.
+ * tasks are executed, but no new tasks will be accepted.
+ * Invocation has no additional effect if already shut down.
+ *
+ * <p>This method does not wait for previously submitted tasks to
+ * complete execution. Use {@link #awaitTermination awaitTermination}
+ * to do that.
+ *
* @throws SecurityException if a security manager exists and
- * shutting down this ExecutorService may manipulate threads that
- * the caller is not permitted to modify because it does not hold
- * {@link java.lang.RuntimePermission}<tt>("modifyThread")</tt>,
- * or the security manager's <tt>checkAccess</tt> method denies access.
+ * shutting down this ExecutorService may manipulate
+ * threads that the caller is not permitted to modify
+ * because it does not hold {@link
+ * java.lang.RuntimePermission}<tt>("modifyThread")</tt>,
+ * or the security manager's <tt>checkAccess</tt> method
+ * denies access.
*/
void shutdown();
/**
* Attempts to stop all actively executing tasks, halts the
- * processing of waiting tasks, and returns a list of the tasks that were
- * awaiting execution.
- *
+ * processing of waiting tasks, and returns a list of the tasks
+ * that were awaiting execution.
+ *
+ * <p>This method does not wait for actively executing tasks to
+ * terminate. Use {@link #awaitTermination awaitTermination} to
+ * do that.
+ *
* <p>There are no guarantees beyond best-effort attempts to stop
* processing actively executing tasks. For example, typical
- * implementations will cancel via {@link Thread#interrupt}, so if any
- * tasks mask or fail to respond to interrupts, they may never terminate.
+ * implementations will cancel via {@link Thread#interrupt}, so any
+ * task that fails to respond to interrupts may never terminate.
*
* @return list of tasks that never commenced execution
* @throws SecurityException if a security manager exists and
- * shutting down this ExecutorService may manipulate threads that
- * the caller is not permitted to modify because it does not hold
- * {@link java.lang.RuntimePermission}<tt>("modifyThread")</tt>,
- * or the security manager's <tt>checkAccess</tt> method denies access.
+ * shutting down this ExecutorService may manipulate
+ * threads that the caller is not permitted to modify
+ * because it does not hold {@link
+ * java.lang.RuntimePermission}<tt>("modifyThread")</tt>,
+ * or the security manager's <tt>checkAccess</tt> method
+ * denies access.
*/
List<Runnable> shutdownNow();
@@ -130,8 +178,8 @@ public interface ExecutorService extends Executor {
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
- * @return <tt>true</tt> if this executor terminated and <tt>false</tt>
- * if the timeout elapsed before termination
+ * @return <tt>true</tt> if this executor terminated and
+ * <tt>false</tt> if the timeout elapsed before termination
* @throws InterruptedException if interrupted while waiting
*/
boolean awaitTermination(long timeout, TimeUnit unit)
@@ -139,8 +187,10 @@ public interface ExecutorService extends Executor {
/**
- * Submits a value-returning task for execution and returns a Future
- * representing the pending results of the task.
+ * Submits a value-returning task for execution and returns a
+ * Future representing the pending results of the task. The
+ * Future's <tt>get</tt> method will return the task's result upon
+ * successful completion.
*
* <p>
* If you would like to immediately block waiting
@@ -154,88 +204,92 @@ public interface ExecutorService extends Executor {
*
* @param task the task to submit
* @return a Future representing pending completion of the task
- * @throws RejectedExecutionException if task cannot be scheduled
- * for execution
- * @throws NullPointerException if task null
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ * @throws NullPointerException if the task is null
*/
<T> Future<T> submit(Callable<T> task);
/**
- * Submits a Runnable task for execution and returns a Future
- * representing that task that will upon completion return
- * the given result
+ * Submits a Runnable task for execution and returns a Future
+ * representing that task. The Future's <tt>get</tt> method will
+ * return the given result upon successful completion.
*
* @param task the task to submit
* @param result the result to return
- * @return a Future representing pending completion of the task,
- * and whose <tt>get()</tt> method will return the given result
- * upon completion.
- * @throws RejectedExecutionException if task cannot be scheduled
- * for execution
- * @throws NullPointerException if task null
+ * @return a Future representing pending completion of the task
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ * @throws NullPointerException if the task is null
*/
<T> Future<T> submit(Runnable task, T result);
/**
- * Submits a Runnable task for execution and returns a Future
- * representing that task.
+ * Submits a Runnable task for execution and returns a Future
+ * representing that task. The Future's <tt>get</tt> method will
+ * return <tt>null</tt> upon <em>successful</em> completion.
*
* @param task the task to submit
- * @return a Future representing pending completion of the task,
- * and whose <tt>get()</tt> method will return <tt>null</tt>
- * upon completion.
- * @throws RejectedExecutionException if task cannot be scheduled
- * for execution
- * @throws NullPointerException if task null
+ * @return a Future representing pending completion of the task
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
+ * @throws NullPointerException if the task is null
*/
Future<?> submit(Runnable task);
/**
- * Executes the given tasks, returning their results
- * when all complete.
+ * Executes the given tasks, returning a list of Futures holding
+ * their status and results when all complete.
+ * {@link Future#isDone} is <tt>true</tt> for each
+ * element of the returned list.
* Note that a <em>completed</em> task could have
* terminated either normally or by throwing an exception.
* The results of this method are undefined if the given
* collection is modified while this operation is in progress.
+ *
* @param tasks the collection of tasks
* @return A list of Futures representing the tasks, in the same
- * sequential order as produced by the iterator for the given task
- * list, each of which has completed.
+ * sequential order as produced by the iterator for the
+ * given task list, each of which has completed.
* @throws InterruptedException if interrupted while waiting, in
- * which case unfinished tasks are cancelled.
+ * which case unfinished tasks are cancelled.
* @throws NullPointerException if tasks or any of its elements are <tt>null</tt>
- * @throws RejectedExecutionException if any task cannot be scheduled
- * for execution
+ * @throws RejectedExecutionException if any task cannot be
+ * scheduled for execution
*/
<T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks)
throws InterruptedException;
/**
- * Executes the given tasks, returning their results
+ * Executes the given tasks, returning a list of Futures holding
+ * their status and results
* when all complete or the timeout expires, whichever happens first.
+ * {@link Future#isDone} is <tt>true</tt> for each
+ * element of the returned list.
* Upon return, tasks that have not completed are cancelled.
* Note that a <em>completed</em> task could have
* terminated either normally or by throwing an exception.
* The results of this method are undefined if the given
* collection is modified while this operation is in progress.
+ *
* @param tasks the collection of tasks
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
- * @return A list of Futures representing the tasks, in the same
- * sequential order as produced by the iterator for the given
- * task list. If the operation did not time out, each task will
- * have completed. If it did time out, some of thiese tasks will
- * not have completed.
+ * @return a list of Futures representing the tasks, in the same
+ * sequential order as produced by the iterator for the
+ * given task list. If the operation did not time out,
+ * each task will have completed. If it did time out, some
+ * of these tasks will not have completed.
* @throws InterruptedException if interrupted while waiting, in
- * which case unfinished tasks are cancelled.
+ * which case unfinished tasks are cancelled
* @throws NullPointerException if tasks, any of its elements, or
- * unit are <tt>null</tt>
+ * unit are <tt>null</tt>
* @throws RejectedExecutionException if any task cannot be scheduled
- * for execution
+ * for execution
*/
- <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks,
- long timeout, TimeUnit unit)
+ <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks,
+ long timeout, TimeUnit unit)
throws InterruptedException;
/**
@@ -245,15 +299,16 @@ public interface ExecutorService extends Executor {
* tasks that have not completed are cancelled.
* The results of this method are undefined if the given
* collection is modified while this operation is in progress.
+ *
* @param tasks the collection of tasks
- * @return The result returned by one of the tasks.
+ * @return the result returned by one of the tasks
* @throws InterruptedException if interrupted while waiting
* @throws NullPointerException if tasks or any of its elements
- * are <tt>null</tt>
- * @throws IllegalArgumentException if tasks empty
+ * are <tt>null</tt>
+ * @throws IllegalArgumentException if tasks is empty
* @throws ExecutionException if no task successfully completes
* @throws RejectedExecutionException if tasks cannot be scheduled
- * for execution
+ * for execution
*/
<T> T invokeAny(Collection<Callable<T>> tasks)
throws InterruptedException, ExecutionException;
@@ -266,21 +321,21 @@ public interface ExecutorService extends Executor {
* completed are cancelled.
* The results of this method are undefined if the given
* collection is modified while this operation is in progress.
+ *
* @param tasks the collection of tasks
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
- * @return The result returned by one of the tasks.
+ * @return the result returned by one of the tasks.
* @throws InterruptedException if interrupted while waiting
* @throws NullPointerException if tasks, any of its elements, or
- * unit are <tt>null</tt>
+ * unit are <tt>null</tt>
* @throws TimeoutException if the given timeout elapses before
- * any task successfully completes
+ * any task successfully completes
* @throws ExecutionException if no task successfully completes
* @throws RejectedExecutionException if tasks cannot be scheduled
- * for execution
+ * for execution
*/
- <T> T invokeAny(Collection<Callable<T>> tasks,
- long timeout, TimeUnit unit)
+ <T> T invokeAny(Collection<Callable<T>> tasks,
+ long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
-
}
diff --git a/concurrent/src/main/java/java/util/concurrent/Executors.java b/concurrent/src/main/java/java/util/concurrent/Executors.java
index 23f1b75..90cf6ee 100644
--- a/concurrent/src/main/java/java/util/concurrent/Executors.java
+++ b/concurrent/src/main/java/java/util/concurrent/Executors.java
@@ -11,6 +11,7 @@ import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
+import java.security.PrivilegedActionException;
import java.security.AccessControlException;
/**
@@ -18,18 +19,18 @@ import java.security.AccessControlException;
* ExecutorService}, {@link ScheduledExecutorService}, {@link
* ThreadFactory}, and {@link Callable} classes defined in this
* package. This class supports the following kinds of methods:
- *
+ *
* <ul>
- * <li> Methods that create and return an {@link ExecutorService}
- * set up with commonly useful configuration settings.
- * <li> Methods that create and return a {@link ScheduledExecutorService}
- * set up with commonly useful configuration settings.
+ * <li> Methods that create and return an {@link ExecutorService}
+ * set up with commonly useful configuration settings.
+ * <li> Methods that create and return a {@link ScheduledExecutorService}
+ * set up with commonly useful configuration settings.
* <li> Methods that create and return a "wrapped" ExecutorService, that
* disables reconfiguration by making implementation-specific methods
* inaccessible.
* <li> Methods that create and return a {@link ThreadFactory}
* that sets newly created threads to a known state.
- * <li> Methods that create and return a {@link Callable}
+ * <li> Methods that create and return a {@link Callable}
* out of other closure-like forms, so they can be used
* in execution methods requiring <tt>Callable</tt>.
* </ul>
@@ -40,14 +41,19 @@ import java.security.AccessControlException;
public class Executors {
/**
- * Creates a thread pool that reuses a fixed set of threads
- * operating off a shared unbounded queue. If any thread
- * terminates due to a failure during execution prior to shutdown,
- * a new one will take its place if needed to execute subsequent
- * tasks.
+ * Creates a thread pool that reuses a fixed number of threads
+ * operating off a shared unbounded queue. At any point, at most
+ * <tt>nThreads</tt> threads will be active processing tasks.
+ * If additional tasks are submitted when all threads are active,
+ * they will wait in the queue until a thread is available.
+ * If any thread terminates due to a failure during execution
+ * prior to shutdown, a new one will take its place if needed to
+ * execute subsequent tasks. The threads in the pool will exist
+ * until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
+ * @throws IllegalArgumentException if <tt>nThreads &lt;= 0</tt>
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
@@ -56,13 +62,23 @@ public class Executors {
}
/**
- * Creates a thread pool that reuses a fixed set of threads
+ * Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue, using the provided
- * ThreadFactory to create new threads when needed.
+ * ThreadFactory to create new threads when needed. At any point,
+ * at most <tt>nThreads</tt> threads will be active processing
+ * tasks. If additional tasks are submitted when all threads are
+ * active, they will wait in the queue until a thread is
+ * available. If any thread terminates due to a failure during
+ * execution prior to shutdown, a new one will take its place if
+ * needed to execute subsequent tasks. The threads in the pool will
+ * exist until it is explicitly {@link ExecutorService#shutdown
+ * shutdown}.
*
* @param nThreads the number of threads in the pool
* @param threadFactory the factory to use when creating new threads
* @return the newly created thread pool
+ * @throws NullPointerException if threadFactory is null
+ * @throws IllegalArgumentException if <tt>nThreads &lt;= 0</tt>
*/
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
@@ -85,7 +101,7 @@ public class Executors {
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
- return new DelegatedExecutorService
+ return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
@@ -95,17 +111,18 @@ public class Executors {
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue, and uses the provided ThreadFactory to
* create a new thread when needed. Unlike the otherwise
- * equivalent <tt>newFixedThreadPool(1, threadFactory)</tt> the returned executor
- * is guaranteed not to be reconfigurable to use additional
- * threads.
- *
+ * equivalent <tt>newFixedThreadPool(1, threadFactory)</tt> the
+ * returned executor is guaranteed not to be reconfigurable to use
+ * additional threads.
+ *
* @param threadFactory the factory to use when creating new
* threads
*
* @return the newly created single-threaded Executor
+ * @throws NullPointerException if threadFactory is null
*/
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
- return new DelegatedExecutorService
+ return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
@@ -141,6 +158,7 @@ public class Executors {
* ThreadFactory to create new threads when needed.
* @param threadFactory the factory to use when creating new threads
* @return the newly created thread pool
+ * @throws NullPointerException if threadFactory is null
*/
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
@@ -148,7 +166,7 @@ public class Executors {
new SynchronousQueue<Runnable>(),
threadFactory);
}
-
+
/**
* Creates a single-threaded executor that can schedule commands
* to run after a given delay, or to execute periodically.
@@ -181,31 +199,35 @@ public class Executors {
* @param threadFactory the factory to use when creating new
* threads
* @return a newly created scheduled executor
+ * @throws NullPointerException if threadFactory is null
*/
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1, threadFactory));
}
-
+
/**
- * Creates a thread pool that can schedule commands to run after a
+ * Creates a thread pool that can schedule commands to run after a
* given delay, or to execute periodically.
* @param corePoolSize the number of threads to keep in the pool,
* even if they are idle.
* @return a newly created scheduled thread pool
+ * @throws IllegalArgumentException if <tt>corePoolSize &lt; 0</tt>
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
/**
- * Creates a thread pool that can schedule commands to run after a
+ * Creates a thread pool that can schedule commands to run after a
* given delay, or to execute periodically.
* @param corePoolSize the number of threads to keep in the pool,
* even if they are idle.
* @param threadFactory the factory to use when the executor
- * creates a new thread.
+ * creates a new thread.
* @return a newly created scheduled thread pool
+ * @throws IllegalArgumentException if <tt>corePoolSize &lt; 0</tt>
+ * @throws NullPointerException if threadFactory is null
*/
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
@@ -244,7 +266,7 @@ public class Executors {
throw new NullPointerException();
return new DelegatedScheduledExecutorService(executor);
}
-
+
/**
* Returns a default thread factory used to create new threads.
* This factory creates all new threads used by an Executor in the
@@ -252,8 +274,9 @@ public class Executors {
* java.lang.SecurityManager}, it uses the group of {@link
* System#getSecurityManager}, else the group of the thread
* invoking this <tt>defaultThreadFactory</tt> method. Each new
- * thread is created as a non-daemon thread with priority
- * <tt>Thread.NORM_PRIORITY</tt>. New threads have names
+ * thread is created as a non-daemon thread with priority set to
+ * the smaller of <tt>Thread.NORM_PRIORITY</tt> and the maximum
+ * priority permitted in the thread group. New threads have names
* accessible via {@link Thread#getName} of
* <em>pool-N-thread-M</em>, where <em>N</em> is the sequence
* number of this factory, and <em>M</em> is the sequence number
@@ -307,8 +330,8 @@ public class Executors {
* <tt>Callable</tt> to an otherwise resultless action.
* @param task the task to run
* @param result the result to return
- * @throws NullPointerException if task null
* @return a callable object
+ * @throws NullPointerException if task null
*/
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
@@ -336,10 +359,11 @@ public class Executors {
* @return a callable object
* @throws NullPointerException if action null
*/
- public static Callable<Object> callable(PrivilegedAction action) {
+ public static Callable<Object> callable(final PrivilegedAction<?> action) {
if (action == null)
throw new NullPointerException();
- return new PrivilegedActionAdapter(action);
+ return new Callable<Object>() {
+ public Object call() { return action.run(); }};
}
/**
@@ -350,10 +374,11 @@ public class Executors {
* @return a callable object
* @throws NullPointerException if action null
*/
- public static Callable<Object> callable(PrivilegedExceptionAction action) {
+ public static Callable<Object> callable(final PrivilegedExceptionAction<?> action) {
if (action == null)
throw new NullPointerException();
- return new PrivilegedExceptionActionAdapter(action);
+ return new Callable<Object>() {
+ public Object call() throws Exception { return action.run(); }};
}
/**
@@ -373,9 +398,9 @@ public class Executors {
public static <T> Callable<T> privilegedCallable(Callable<T> callable) {
if (callable == null)
throw new NullPointerException();
- return new PrivilegedCallable(callable);
+ return new PrivilegedCallable<T>(callable);
}
-
+
/**
* Returns a {@link Callable} object that will, when
* called, execute the given <tt>callable</tt> under the current
@@ -397,7 +422,7 @@ public class Executors {
public static <T> Callable<T> privilegedCallableUsingCurrentClassLoader(Callable<T> callable) {
if (callable == null)
throw new NullPointerException();
- return new PrivilegedCallableUsingCurrentClassLoader(callable);
+ return new PrivilegedCallableUsingCurrentClassLoader<T>(callable);
}
// Non-public classes supporting the public methods
@@ -408,71 +433,39 @@ public class Executors {
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
- RunnableAdapter(Runnable task, T result) {
- this.task = task;
+ RunnableAdapter(Runnable task, T result) {
+ this.task = task;
this.result = result;
}
- public T call() {
- task.run();
- return result;
- }
- }
-
- /**
- * A callable that runs given privileged action and returns its result
- */
- static final class PrivilegedActionAdapter implements Callable<Object> {
- PrivilegedActionAdapter(PrivilegedAction action) {
- this.action = action;
- }
- public Object call () {
- return action.run();
- }
- private final PrivilegedAction action;
- }
-
- /**
- * A callable that runs given privileged exception action and returns its result
- */
- static final class PrivilegedExceptionActionAdapter implements Callable<Object> {
- PrivilegedExceptionActionAdapter(PrivilegedExceptionAction action) {
- this.action = action;
- }
- public Object call () throws Exception {
- return action.run();
+ public T call() {
+ task.run();
+ return result;
}
- private final PrivilegedExceptionAction action;
}
-
/**
* A callable that runs under established access control settings
*/
static final class PrivilegedCallable<T> implements Callable<T> {
- private final AccessControlContext acc;
private final Callable<T> task;
- private T result;
- private Exception exception;
+ private final AccessControlContext acc;
+
PrivilegedCallable(Callable<T> task) {
this.task = task;
this.acc = AccessController.getContext();
}
public T call() throws Exception {
- AccessController.doPrivileged(new PrivilegedAction() {
- public Object run() {
- try {
- result = task.call();
- } catch(Exception ex) {
- exception = ex;
+ try {
+ return AccessController.doPrivileged(
+ new PrivilegedExceptionAction<T>() {
+ public T run() throws Exception {
+ return task.call();
}
- return null;
- }
- }, acc);
- if (exception != null)
- throw exception;
- else
- return result;
+ }, acc);
+ } catch (PrivilegedActionException e) {
+ throw e.getException();
+ }
}
}
@@ -481,44 +474,50 @@ public class Executors {
* current ClassLoader
*/
static final class PrivilegedCallableUsingCurrentClassLoader<T> implements Callable<T> {
- private final ClassLoader ccl;
- private final AccessControlContext acc;
private final Callable<T> task;
- private T result;
- private Exception exception;
+ private final AccessControlContext acc;
+ private final ClassLoader ccl;
+
PrivilegedCallableUsingCurrentClassLoader(Callable<T> task) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ // Calls to getContextClassLoader from this class
+ // never trigger a security check, but we check
+ // whether our callers have this permission anyways.
+ sm.checkPermission(new RuntimePermission("getContextClassLoader"));
+
+ // Whether setContextClassLoader turns out to be necessary
+ // or not, we fail fast if permission is not available.
+ sm.checkPermission(new RuntimePermission("setContextClassLoader"));
+ }
this.task = task;
- this.ccl = Thread.currentThread().getContextClassLoader();
this.acc = AccessController.getContext();
- acc.checkPermission(new RuntimePermission("getContextClassLoader"));
- acc.checkPermission(new RuntimePermission("setContextClassLoader"));
+ this.ccl = Thread.currentThread().getContextClassLoader();
}
public T call() throws Exception {
- AccessController.doPrivileged(new PrivilegedAction() {
- public Object run() {
- ClassLoader savedcl = null;
- Thread t = Thread.currentThread();
- try {
- ClassLoader cl = t.getContextClassLoader();
- if (ccl != cl) {
- t.setContextClassLoader(ccl);
- savedcl = cl;
+ try {
+ return AccessController.doPrivileged(
+ new PrivilegedExceptionAction<T>() {
+ public T run() throws Exception {
+ ClassLoader savedcl = null;
+ Thread t = Thread.currentThread();
+ try {
+ ClassLoader cl = t.getContextClassLoader();
+ if (ccl != cl) {
+ t.setContextClassLoader(ccl);
+ savedcl = cl;
+ }
+ return task.call();
+ } finally {
+ if (savedcl != null)
+ t.setContextClassLoader(savedcl);
}
- result = task.call();
- } catch(Exception ex) {
- exception = ex;
- } finally {
- if (savedcl != null)
- t.setContextClassLoader(savedcl);
}
- return null;
- }
- }, acc);
- if (exception != null)
- throw exception;
- else
- return result;
+ }, acc);
+ } catch (PrivilegedActionException e) {
+ throw e.getException();
+ }
}
}
@@ -526,22 +525,22 @@ public class Executors {
* The default thread factory
*/
static class DefaultThreadFactory implements ThreadFactory {
- static final AtomicInteger poolNumber = new AtomicInteger(1);
- final ThreadGroup group;
- final AtomicInteger threadNumber = new AtomicInteger(1);
- final String namePrefix;
+ private static final AtomicInteger poolNumber = new AtomicInteger(1);
+ private final ThreadGroup group;
+ private final AtomicInteger threadNumber = new AtomicInteger(1);
+ private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null)? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
- namePrefix = "pool-" +
- poolNumber.getAndIncrement() +
+ namePrefix = "pool-" +
+ poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
- Thread t = new Thread(group, r,
+ Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
@@ -553,38 +552,46 @@ public class Executors {
}
/**
- * Thread factory capturing access control and class loader
+ * Thread factory capturing access control context and class loader
*/
static class PrivilegedThreadFactory extends DefaultThreadFactory {
- private final ClassLoader ccl;
private final AccessControlContext acc;
+ private final ClassLoader ccl;
PrivilegedThreadFactory() {
super();
- this.ccl = Thread.currentThread().getContextClassLoader();
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ // Calls to getContextClassLoader from this class
+ // never trigger a security check, but we check
+ // whether our callers have this permission anyways.
+ sm.checkPermission(new RuntimePermission("getContextClassLoader"));
+
+ // Fail fast
+ sm.checkPermission(new RuntimePermission("setContextClassLoader"));
+ }
this.acc = AccessController.getContext();
- acc.checkPermission(new RuntimePermission("setContextClassLoader"));
+ this.ccl = Thread.currentThread().getContextClassLoader();
}
-
+
public Thread newThread(final Runnable r) {
return super.newThread(new Runnable() {
public void run() {
- AccessController.doPrivileged(new PrivilegedAction() {
- public Object run() {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
Thread.currentThread().setContextClassLoader(ccl);
r.run();
- return null;
+ return null;
}
}, acc);
}
});
}
-
}
- /**
+ /**
* A wrapper class that exposes only the ExecutorService methods
- * of an implementation.
+ * of an ExecutorService implementation.
*/
static class DelegatedExecutorService extends AbstractExecutorService {
private final ExecutorService e;
@@ -611,8 +618,8 @@ public class Executors {
throws InterruptedException {
return e.invokeAll(tasks);
}
- public <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks,
- long timeout, TimeUnit unit)
+ public <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks,
+ long timeout, TimeUnit unit)
throws InterruptedException {
return e.invokeAll(tasks, timeout, unit);
}
@@ -620,19 +627,29 @@ public class Executors {
throws InterruptedException, ExecutionException {
return e.invokeAny(tasks);
}
- public <T> T invokeAny(Collection<Callable<T>> tasks,
- long timeout, TimeUnit unit)
+ public <T> T invokeAny(Collection<Callable<T>> tasks,
+ long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return e.invokeAny(tasks, timeout, unit);
}
}
-
+
+ static class FinalizableDelegatedExecutorService
+ extends DelegatedExecutorService {
+ FinalizableDelegatedExecutorService(ExecutorService executor) {
+ super(executor);
+ }
+ protected void finalize() {
+ super.shutdown();
+ }
+ }
+
/**
- * A wrapper class that exposes only the ExecutorService and
- * ScheduleExecutor methods of a ScheduledExecutorService implementation.
+ * A wrapper class that exposes only the ScheduledExecutorService
+ * methods of a ScheduledExecutorService implementation.
*/
static class DelegatedScheduledExecutorService
- extends DelegatedExecutorService
+ extends DelegatedExecutorService
implements ScheduledExecutorService {
private final ScheduledExecutorService e;
DelegatedScheduledExecutorService(ScheduledExecutorService executor) {
@@ -653,7 +670,7 @@ public class Executors {
}
}
-
+
/** Cannot instantiate. */
private Executors() {}
}
diff --git a/concurrent/src/main/java/java/util/concurrent/Future.java b/concurrent/src/main/java/java/util/concurrent/Future.java
index 38fa8c6..0459ee4 100644
--- a/concurrent/src/main/java/java/util/concurrent/Future.java
+++ b/concurrent/src/main/java/java/util/concurrent/Future.java
@@ -29,10 +29,13 @@ package java.util.concurrent;
* class App {
* ExecutorService executor = ...
* ArchiveSearcher searcher = ...
- * void showSearch(final String target) throws InterruptedException {
- * Future&lt;String&gt; future = executor.submit(new Callable&lt;String&gt;() {
- * public String call() { return searcher.search(target); }
- * });
+ * void showSearch(final String target)
+ * throws InterruptedException {
+ * Future&lt;String&gt; future
+ * = executor.submit(new Callable&lt;String&gt;() {
+ * public String call() {
+ * return searcher.search(target);
+ * }});
* displayOtherThings(); // do other things while searching
* try {
* displayText(future.get()); // use future
@@ -41,8 +44,8 @@ package java.util.concurrent;
* }
* </pre>
*
- * The {@link FutureTask} class is an implementation of <tt>Future</tt> that
- * implements <tt>Runnable</tt>, and so may be executed by an <tt>Executor</tt>.
+ * The {@link FutureTask} class is an implementation of <tt>Future</tt> that
+ * implements <tt>Runnable</tt>, and so may be executed by an <tt>Executor</tt>.
* For example, the above construction with <tt>submit</tt> could be replaced by:
* <pre>
* FutureTask&lt;String&gt; future =
@@ -52,6 +55,11 @@ package java.util.concurrent;
* }});
* executor.execute(future);
* </pre>
+ *
+ * <p>Memory consistency effects: Actions taken by the asynchronous computation
+ * <a href="package-summary.html#MemoryVisibility"> <i>happen-before</i></a>
+ * actions following the corresponding {@code Future.get()} in another thread.
+ *
* @see FutureTask
* @see Executor
* @since 1.5
@@ -62,7 +70,7 @@ public interface Future<V> {
/**
* Attempts to cancel execution of this task. This attempt will
- * fail if the task has already completed, already been cancelled,
+ * fail if the task has already completed, has already been cancelled,
* or could not be cancelled for some other reason. If successful,
* and this task has not started when <tt>cancel</tt> is called,
* this task should never run. If the task has already started,
@@ -70,6 +78,10 @@ public interface Future<V> {
* whether the thread executing this task should be interrupted in
* an attempt to stop the task.
*
+ * <p>After this method returns, subsequent calls to {@link #isDone} will
+ * always return <tt>true</tt>. Subsequent calls to {@link #isCancelled}
+ * will always return <tt>true</tt> if this method returned <tt>true</tt>.
+ *
* @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
* task should be interrupted; otherwise, in-progress tasks are allowed
* to complete
@@ -83,18 +95,18 @@ public interface Future<V> {
* Returns <tt>true</tt> if this task was cancelled before it completed
* normally.
*
- * @return <tt>true</tt> if task was cancelled before it completed
+ * @return <tt>true</tt> if this task was cancelled before it completed
*/
boolean isCancelled();
/**
- * Returns <tt>true</tt> if this task completed.
+ * Returns <tt>true</tt> if this task completed.
*
* Completion may be due to normal termination, an exception, or
* cancellation -- in all of these cases, this method will return
* <tt>true</tt>.
- *
- * @return <tt>true</tt> if this task completed.
+ *
+ * @return <tt>true</tt> if this task completed
*/
boolean isDone();
@@ -128,6 +140,3 @@ public interface Future<V> {
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
-
-
-
diff --git a/concurrent/src/main/java/java/util/concurrent/FutureTask.java b/concurrent/src/main/java/java/util/concurrent/FutureTask.java
index 87db1cd..8aa9dd1 100644
--- a/concurrent/src/main/java/java/util/concurrent/FutureTask.java
+++ b/concurrent/src/main/java/java/util/concurrent/FutureTask.java
@@ -35,7 +35,7 @@ public class FutureTask<V> implements Future<V>, Runnable {
private final Sync sync;
/**
- * Creates a <tt>FutureTask</tt> that will upon running, execute the
+ * Creates a <tt>FutureTask</tt> that will, upon running, execute the
* given <tt>Callable</tt>.
*
* @param callable the callable task
@@ -48,11 +48,11 @@ public class FutureTask<V> implements Future<V>, Runnable {
}
/**
- * Creates a <tt>FutureTask</tt> that will upon running, execute the
+ * Creates a <tt>FutureTask</tt> that will, upon running, execute the
* given <tt>Runnable</tt>, and arrange that <tt>get</tt> will return the
* given result on successful completion.
*
- * @param runnable the runnable task
+ * @param runnable the runnable task
* @param result the result to return on successful completion. If
* you don't need a particular result, consider using
* constructions of the form:
@@ -66,7 +66,7 @@ public class FutureTask<V> implements Future<V>, Runnable {
public boolean isCancelled() {
return sync.innerIsCancelled();
}
-
+
public boolean isDone() {
return sync.innerIsDone();
}
@@ -74,11 +74,17 @@ public class FutureTask<V> implements Future<V>, Runnable {
public boolean cancel(boolean mayInterruptIfRunning) {
return sync.innerCancel(mayInterruptIfRunning);
}
-
+
+ /**
+ * @throws CancellationException {@inheritDoc}
+ */
public V get() throws InterruptedException, ExecutionException {
return sync.innerGet();
}
+ /**
+ * @throws CancellationException {@inheritDoc}
+ */
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return sync.innerGet(unit.toNanos(timeout));
@@ -98,8 +104,10 @@ public class FutureTask<V> implements Future<V>, Runnable {
/**
* Sets the result of this Future to the given value unless
* this future has already been set or has been cancelled.
+ * This method is invoked internally by the <tt>run</tt> method
+ * upon successful completion of the computation.
* @param v the value
- */
+ */
protected void set(V v) {
sync.innerSet(v);
}
@@ -108,15 +116,22 @@ public class FutureTask<V> implements Future<V>, Runnable {
* Causes this future to report an <tt>ExecutionException</tt>
* with the given throwable as its cause, unless this Future has
* already been set or has been cancelled.
- * @param t the cause of failure.
- */
+ * This method is invoked internally by the <tt>run</tt> method
+ * upon failure of the computation.
+ * @param t the cause of failure
+ */
protected void setException(Throwable t) {
sync.innerSetException(t);
}
-
+
+ // The following (duplicated) doc comment can be removed once
+ //
+ // 6270645: Javadoc comments should be inherited from most derived
+ // superinterface or superclass
+ // is fixed.
/**
- * Sets this Future to the result of computation unless
- * it has been cancelled.
+ * Sets this Future to the result of its computation
+ * unless it has been cancelled.
*/
public void run() {
sync.innerRun();
@@ -143,6 +158,10 @@ public class FutureTask<V> implements Future<V>, Runnable {
* Uses AQS sync state to represent run status
*/
private final class Sync extends AbstractQueuedSynchronizer {
+ private static final long serialVersionUID = -7828117401763700385L;
+
+ /** State value representing that task is ready to run */
+ private static final int READY = 0;
/** State value representing that task is running */
private static final int RUNNING = 1;
/** State value representing that task ran */
@@ -157,10 +176,10 @@ public class FutureTask<V> implements Future<V>, Runnable {
/** The exception to throw from get() */
private Throwable exception;
- /**
+ /**
* The thread running task. When nulled after set/cancel, this
* indicates that the results are accessible. Must be
- * volatile, to serve as write barrier on completion.
+ * volatile, to ensure visibility upon completion.
*/
private volatile Thread runner;
@@ -176,7 +195,7 @@ public class FutureTask<V> implements Future<V>, Runnable {
* Implements AQS base acquire to succeed if ran or cancelled
*/
protected int tryAcquireShared(int ignore) {
- return innerIsDone()? 1 : -1;
+ return innerIsDone() ? 1 : -1;
}
/**
@@ -185,13 +204,13 @@ public class FutureTask<V> implements Future<V>, Runnable {
*/
protected boolean tryReleaseShared(int ignore) {
runner = null;
- return true;
+ return true;
}
boolean innerIsCancelled() {
return getState() == CANCELLED;
}
-
+
boolean innerIsDone() {
return ranOrCancelled(getState()) && runner == null;
}
@@ -207,7 +226,7 @@ public class FutureTask<V> implements Future<V>, Runnable {
V innerGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException {
if (!tryAcquireSharedNanos(0, nanosTimeout))
- throw new TimeoutException();
+ throw new TimeoutException();
if (getState() == CANCELLED)
throw new CancellationException();
if (exception != null)
@@ -216,28 +235,55 @@ public class FutureTask<V> implements Future<V>, Runnable {
}
void innerSet(V v) {
- int s = getState();
- if (ranOrCancelled(s) || !compareAndSetState(s, RAN))
- return;
- result = v;
- releaseShared(0);
- done();
+ for (;;) {
+ int s = getState();
+ if (s == RAN)
+ return;
+ if (s == CANCELLED) {
+ // aggressively release to set runner to null,
+ // in case we are racing with a cancel request
+ // that will try to interrupt runner
+ releaseShared(0);
+ return;
+ }
+ if (compareAndSetState(s, RAN)) {
+ result = v;
+ releaseShared(0);
+ done();
+ return;
+ }
+ }
}
void innerSetException(Throwable t) {
- int s = getState();
- if (ranOrCancelled(s) || !compareAndSetState(s, RAN))
- return;
- exception = t;
- result = null;
- releaseShared(0);
- done();
+ for (;;) {
+ int s = getState();
+ if (s == RAN)
+ return;
+ if (s == CANCELLED) {
+ // aggressively release to set runner to null,
+ // in case we are racing with a cancel request
+ // that will try to interrupt runner
+ releaseShared(0);
+ return;
+ }
+ if (compareAndSetState(s, RAN)) {
+ exception = t;
+ releaseShared(0);
+ done();
+ return;
+ }
+ }
}
boolean innerCancel(boolean mayInterruptIfRunning) {
- int s = getState();
- if (ranOrCancelled(s) || !compareAndSetState(s, CANCELLED))
- return false;
+ for (;;) {
+ int s = getState();
+ if (ranOrCancelled(s))
+ return false;
+ if (compareAndSetState(s, CANCELLED))
+ break;
+ }
if (mayInterruptIfRunning) {
Thread r = runner;
if (r != null)
@@ -249,28 +295,37 @@ public class FutureTask<V> implements Future<V>, Runnable {
}
void innerRun() {
- if (!compareAndSetState(0, RUNNING))
+ if (!compareAndSetState(READY, RUNNING))
return;
- try {
- runner = Thread.currentThread();
- innerSet(callable.call());
- } catch(Throwable ex) {
- innerSetException(ex);
- }
+
+ runner = Thread.currentThread();
+ if (getState() == RUNNING) { // recheck after setting thread
+ V result;
+ try {
+ result = callable.call();
+ } catch (Throwable ex) {
+ setException(ex);
+ return;
+ }
+ set(result);
+ } else {
+ releaseShared(0); // cancel
+ }
}
boolean innerRunAndReset() {
- if (!compareAndSetState(0, RUNNING))
+ if (!compareAndSetState(READY, RUNNING))
return false;
try {
runner = Thread.currentThread();
- callable.call(); // don't set result
+ if (getState() == RUNNING)
+ callable.call(); // don't set result
runner = null;
- return compareAndSetState(RUNNING, 0);
- } catch(Throwable ex) {
- innerSetException(ex);
+ return compareAndSetState(RUNNING, READY);
+ } catch (Throwable ex) {
+ setException(ex);
return false;
- }
+ }
}
}
}
diff --git a/concurrent/src/main/java/java/util/concurrent/Java6Arrays.java b/concurrent/src/main/java/java/util/concurrent/Java6Arrays.java
new file mode 100644
index 0000000..6b728be
--- /dev/null
+++ b/concurrent/src/main/java/java/util/concurrent/Java6Arrays.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.util.concurrent;
+
+import java.lang.reflect.Array;
+
+/**
+ * Arrays.copyOf and Arrays.copyOfRange backported from Java 6.
+ */
+class Java6Arrays {
+
+ static <T> T[] copyOf(T[] original, int newLength) {
+ if (null == original) {
+ throw new NullPointerException();
+ }
+ if (0 <= newLength) {
+ return copyOfRange(original, 0, newLength);
+ }
+ throw new NegativeArraySizeException();
+ }
+
+ static <T, U> T[] copyOf(U[] original, int newLength,
+ Class<? extends T[]> newType) {
+ if (0 <= newLength) {
+ return copyOfRange(original, 0, newLength, newType);
+ }
+ throw new NegativeArraySizeException();
+ }
+
+ @SuppressWarnings("unchecked")
+ static <T> T[] copyOfRange(T[] original, int start, int end) {
+ if (original.length >= start && 0 <= start) {
+ if (start <= end) {
+ int length = end - start;
+ int copyLength = Math.min(length, original.length - start);
+ T[] copy = (T[]) Array.newInstance(original.getClass().getComponentType(), length);
+ System.arraycopy(original, start, copy, 0, copyLength);
+ return copy;
+ }
+ throw new IllegalArgumentException();
+ }
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ @SuppressWarnings("unchecked")
+ static <T, U> T[] copyOfRange(U[] original, int start, int end,
+ Class<? extends T[]> newType) {
+ if (start <= end) {
+ if (original.length >= start && 0 <= start) {
+ int length = end - start;
+ int copyLength = Math.min(length, original.length - start);
+ T[] copy = (T[]) Array.newInstance(newType.getComponentType(),
+ length);
+ System.arraycopy(original, start, copy, 0, copyLength);
+ return copy;
+ }
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ throw new IllegalArgumentException();
+ }
+
+}
diff --git a/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java b/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java
index 662f63e..e06f7bd 100644
--- a/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java
+++ b/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java
@@ -32,14 +32,15 @@ import java.util.*;
* dynamically created upon each insertion unless this would bring the
* queue above capacity.
*
- * <p>This class implements all of the <em>optional</em> methods
- * of the {@link Collection} and {@link Iterator} interfaces.
+ * <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 LinkedBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
private static final long serialVersionUID = -6903933977591709194L;
@@ -56,7 +57,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
* items have been entered since the signal. And symmetrically for
* takes signalling puts. Operations such as remove(Object) and
* iterators acquire both locks.
- */
+ */
/**
* Linked list node class
@@ -93,7 +94,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
private final Condition notFull = putLock.newCondition();
/**
- * Signal a waiting take. Called only from put/offer (which do not
+ * Signals a waiting take. Called only from put/offer (which do not
* otherwise ordinarily lock takeLock.)
*/
private void signalNotEmpty() {
@@ -107,7 +108,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
}
/**
- * Signal a waiting put. Called only from take/poll.
+ * Signals a waiting put. Called only from take/poll.
*/
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
@@ -120,7 +121,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
}
/**
- * Create a node and link it at end of queue
+ * Creates a node and links it at end of queue.
* @param x the item
*/
private void insert(E x) {
@@ -128,11 +129,13 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
}
/**
- * Remove a node from head of queue,
+ * Removes a node from head of queue,
* @return the node
*/
private E extract() {
- Node<E> first = head.next;
+ Node<E> h = head;
+ Node<E> first = h.next;
+ h.next = null; // help GC
head = first;
E x = first.item;
first.item = null;
@@ -167,9 +170,9 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
/**
* Creates a <tt>LinkedBlockingQueue</tt> with the given (fixed) capacity.
*
- * @param capacity the capacity of this queue.
+ * @param capacity the capacity of this queue
* @throws IllegalArgumentException if <tt>capacity</tt> is not greater
- * than zero.
+ * than zero
*/
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
@@ -182,14 +185,15 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
* {@link Integer#MAX_VALUE}, initially containing the elements of the
* given collection,
* added in traversal order of the collection's iterator.
+ *
* @param c the collection of elements to initially contain
- * @throws NullPointerException if <tt>c</tt> or any element within it
- * is <tt>null</tt>
+ * @throws NullPointerException if the specified collection or any
+ * of its elements are null
*/
public LinkedBlockingQueue(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
- for (Iterator<? extends E> it = c.iterator(); it.hasNext();)
- add(it.next());
+ for (E e : c)
+ add(e);
}
@@ -198,7 +202,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
/**
* Returns the number of elements in this queue.
*
- * @return the number of elements in this queue.
+ * @return the number of elements in this queue
*/
public int size() {
return count.get();
@@ -207,31 +211,29 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
// this doc comment is a modified copy of the inherited doc comment,
// without the reference to unlimited queues.
/**
- * Returns the number of elements that this queue can ideally (in
- * the absence of memory or resource constraints) accept without
+ * 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 <tt>add</tt> an element will succeed by
- * inspecting <tt>remainingCapacity</tt> because it may be the
- * case that a waiting consumer is ready to <tt>take</tt> an
- * element out of an otherwise full queue.
- *
- * @return the remaining capacity
+ *
+ * <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() {
return capacity - count.get();
}
/**
- * Adds the specified element to the tail of this queue, waiting if
+ * Inserts the specified element at the tail of this queue, waiting if
* necessary for space to become available.
- * @param o the element to add
- * @throws InterruptedException if interrupted while waiting.
- * @throws NullPointerException if the specified element is <tt>null</tt>.
+ *
+ * @throws InterruptedException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
*/
- public void put(E o) throws InterruptedException {
- if (o == null) throw new NullPointerException();
+ public void put(E e) throws InterruptedException {
+ if (e == null) throw new NullPointerException();
// Note: convention in all put/take/etc is to preset
// local var holding count negative to indicate failure unless set.
int c = -1;
@@ -255,7 +257,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
notFull.signal(); // propagate to a non-interrupted thread
throw ie;
}
- insert(o);
+ insert(e);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
@@ -269,20 +271,16 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
/**
* Inserts the specified element at the tail of this queue, waiting if
* necessary up to the specified wait time for space to become available.
- * @param o the element to add
- * @param timeout how long to wait before giving up, in units of
- * <tt>unit</tt>
- * @param unit a <tt>TimeUnit</tt> determining how to interpret the
- * <tt>timeout</tt> parameter
+ *
* @return <tt>true</tt> if successful, or <tt>false</tt> if
- * the specified waiting time elapses before space is available.
- * @throws InterruptedException if interrupted while waiting.
- * @throws NullPointerException if the specified element is <tt>null</tt>.
+ * the specified waiting time elapses before space is available.
+ * @throws InterruptedException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
*/
- public boolean offer(E o, long timeout, TimeUnit unit)
+ public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
- if (o == null) throw new NullPointerException();
+ if (e == null) throw new NullPointerException();
long nanos = unit.toNanos(timeout);
int c = -1;
final ReentrantLock putLock = this.putLock;
@@ -291,7 +289,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
try {
for (;;) {
if (count.get() < capacity) {
- insert(o);
+ insert(e);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
@@ -315,16 +313,18 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
}
/**
- * Inserts the specified element at the tail of this queue if possible,
- * returning immediately if this queue is full.
+ * 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.
+ * When using a capacity-restricted queue, this method is generally
+ * preferable to method {@link BlockingQueue#add add}, which can fail to
+ * insert an element only by throwing an exception.
*
- * @param o the element to add.
- * @return <tt>true</tt> if it was possible to add the element to
- * this queue, else <tt>false</tt>
- * @throws NullPointerException if the specified element is <tt>null</tt>
+ * @throws NullPointerException if the specified element is null
*/
- public boolean offer(E o) {
- if (o == null) throw new NullPointerException();
+ public boolean offer(E e) {
+ if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;
if (count.get() == capacity)
return false;
@@ -333,7 +333,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
putLock.lock();
try {
if (count.get() < capacity) {
- insert(o);
+ insert(e);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
@@ -447,6 +447,17 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
}
}
+ /**
+ * 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;
boolean removed = false;
@@ -465,6 +476,8 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
if (removed) {
p.item = null;
trail.next = p.next;
+ if (last == p)
+ last = trail;
if (count.getAndDecrement() == capacity)
notFull.signalAll();
}
@@ -474,6 +487,19 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
return removed;
}
+ /**
+ * 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() {
fullyLock();
try {
@@ -488,6 +514,42 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
}
}
+ /**
+ * 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) {
fullyLock();
try {
@@ -499,6 +561,8 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
int k = 0;
for (Node p = head.next; p != null; p = p.next)
a[k++] = (T)p.item;
+ if (a.length > k)
+ a[k] = null;
return a;
} finally {
fullyUnlock();
@@ -514,10 +578,16 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
}
}
+ /**
+ * Atomically removes all of the elements from this queue.
+ * The queue will be empty after this call returns.
+ */
public void clear() {
fullyLock();
try {
head.next = null;
+ assert head.item == null;
+ last = head;
if (count.getAndSet(0) == capacity)
notFull.signalAll();
} finally {
@@ -525,16 +595,24 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
}
}
+ /**
+ * @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();
- Node first;
+ Node<E> first;
fullyLock();
try {
first = head.next;
head.next = null;
+ assert head.item == null;
+ last = head;
if (count.getAndSet(0) == capacity)
notFull.signalAll();
} finally {
@@ -549,14 +627,18 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
}
return n;
}
-
+
+ /**
+ * @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;
fullyLock();
try {
int n = 0;
@@ -569,6 +651,9 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
}
if (n != 0) {
head.next = p;
+ assert head.item == null;
+ if (p == null)
+ last = head;
if (count.getAndAdd(-n) == capacity)
notFull.signalAll();
}
@@ -581,12 +666,12 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
/**
* 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 java.util.ConcurrentModificationException},
+ * 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.
+ * @return an iterator over the elements in this queue in proper sequence
*/
public Iterator<E> iterator() {
return new Itr();
@@ -660,6 +745,8 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
if (p == node) {
p.item = null;
trail.next = p.next;
+ if (last == p)
+ last = trail;
int c = count.getAndDecrement();
if (c == capacity)
notFull.signalAll();
diff --git a/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java b/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
index 83f1040..7a33dc7 100644
--- a/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
+++ b/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
@@ -19,16 +19,47 @@ import java.util.*;
* blocking retrieval operations. While this queue is logically
* unbounded, attempted additions may fail due to resource exhaustion
* (causing <tt>OutOfMemoryError</tt>). This class does not permit
- * <tt>null</tt> elements. A priority queue relying on natural
- * ordering also does not permit insertion of non-comparable objects
- * (doing so results in <tt>ClassCastException</tt>).
+ * <tt>null</tt> elements. A priority queue relying on {@linkplain
+ * Comparable natural ordering} also does not permit insertion of
+ * non-comparable objects (doing so results in
+ * <tt>ClassCastException</tt>).
*
- * <p>This class implements all of the <em>optional</em> methods
- * of the {@link Collection} and {@link Iterator} interfaces.
- * <p>The Iterator provided in method {@link #iterator()} is
- * <em>not</em> guaranteed to traverse the elements of the
- * PriorityBlockingQueue in any particular order. If you need ordered
- * traversal, consider using <tt>Arrays.sort(pq.toArray())</tt>.
+ * <p>This class and its iterator implement all of the
+ * <em>optional</em> methods of the {@link Collection} and {@link
+ * Iterator} interfaces. The Iterator provided in method {@link
+ * #iterator()} is <em>not</em> guaranteed to traverse the elements of
+ * the PriorityBlockingQueue in any particular order. If you need
+ * ordered traversal, consider using
+ * <tt>Arrays.sort(pq.toArray())</tt>. Also, method <tt>drainTo</tt>
+ * can be used to <em>remove</em> some or all elements in priority
+ * order and place them in another collection.
+ *
+ * <p>Operations on this class make no guarantees about the ordering
+ * of elements with equal priority. If you need to enforce an
+ * ordering, you can define custom classes or comparators that use a
+ * secondary key to break ties in primary priority values. For
+ * example, here is a class that applies first-in-first-out
+ * tie-breaking to comparable elements. To use it, you would insert a
+ * <tt>new FIFOEntry(anEntry)</tt> instead of a plain entry object.
+ *
+ * <pre>
+ * class FIFOEntry&lt;E extends Comparable&lt;? super E&gt;&gt;
+ * implements Comparable&lt;FIFOEntry&lt;E&gt;&gt; {
+ * final static AtomicLong seq = new AtomicLong();
+ * final long seqNum;
+ * final E entry;
+ * public FIFOEntry(E entry) {
+ * seqNum = seq.getAndIncrement();
+ * this.entry = entry;
+ * }
+ * public E getEntry() { return entry; }
+ * public int compareTo(FIFOEntry&lt;E&gt; other) {
+ * int res = entry.compareTo(other.entry);
+ * if (res == 0 &amp;&amp; other.entry != this.entry)
+ * res = (seqNum &lt; other.seqNum ? -1 : 1);
+ * return res;
+ * }
+ * }</pre>
*
* @since 1.5
* @author Doug Lea
@@ -43,24 +74,22 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
private final Condition notEmpty = lock.newCondition();
/**
- * Creates a <tt>PriorityBlockingQueue</tt> with the default initial
- * capacity
- * (11) that orders its elements according to their natural
- * ordering (using <tt>Comparable</tt>).
+ * Creates a <tt>PriorityBlockingQueue</tt> with the default
+ * initial capacity (11) that orders its elements according to
+ * their {@linkplain Comparable natural ordering}.
*/
public PriorityBlockingQueue() {
q = new PriorityQueue<E>();
}
/**
- * Creates a <tt>PriorityBlockingQueue</tt> with the specified initial
- * capacity
- * that orders its elements according to their natural ordering
- * (using <tt>Comparable</tt>).
+ * Creates a <tt>PriorityBlockingQueue</tt> with the specified
+ * initial capacity that orders its elements according to their
+ * {@linkplain Comparable natural ordering}.
*
- * @param initialCapacity the initial capacity for this priority queue.
+ * @param initialCapacity the initial capacity for this priority queue
* @throws IllegalArgumentException if <tt>initialCapacity</tt> is less
- * than 1
+ * than 1
*/
public PriorityBlockingQueue(int initialCapacity) {
q = new PriorityQueue<E>(initialCapacity, null);
@@ -68,15 +97,15 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
/**
* Creates a <tt>PriorityBlockingQueue</tt> with the specified initial
- * capacity
- * that orders its elements according to the specified comparator.
+ * capacity that orders its elements according to the specified
+ * comparator.
*
- * @param initialCapacity the initial capacity for this priority queue.
- * @param comparator the comparator used to order this priority queue.
- * If <tt>null</tt> then the order depends on the elements' natural
- * ordering.
+ * @param initialCapacity the initial capacity for this priority queue
+ * @param comparator the comparator that will be used to order this
+ * priority queue. If {@code null}, the {@linkplain Comparable
+ * natural ordering} of the elements will be used.
* @throws IllegalArgumentException if <tt>initialCapacity</tt> is less
- * than 1
+ * than 1
*/
public PriorityBlockingQueue(int initialCapacity,
Comparator<? super E> comparator) {
@@ -85,75 +114,54 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
/**
* Creates a <tt>PriorityBlockingQueue</tt> containing the elements
- * in the specified collection. The priority queue has an initial
- * capacity of 110% of the size of the specified collection. If
- * the specified collection is a {@link SortedSet} or a {@link
- * PriorityQueue}, this priority queue will be sorted according to
- * the same comparator, or according to its elements' natural
- * order if the collection is sorted according to its elements'
- * natural order. Otherwise, this priority queue is ordered
- * according to its elements' natural order.
+ * in the specified collection. If the specified collection is a
+ * {@link SortedSet} or a {@link PriorityQueue}, this
+ * priority queue will be ordered according to the same ordering.
+ * Otherwise, this priority queue will be ordered according to the
+ * {@linkplain Comparable natural ordering} of its elements.
*
- * @param c the collection whose elements are to be placed
- * into this priority queue.
+ * @param c the collection whose elements are to be placed
+ * into this priority queue
* @throws ClassCastException if elements of the specified collection
* cannot be compared to one another according to the priority
- * queue's ordering.
- * @throws NullPointerException if <tt>c</tt> or any element within it
- * is <tt>null</tt>
+ * queue's ordering
+ * @throws NullPointerException if the specified collection or any
+ * of its elements are null
*/
public PriorityBlockingQueue(Collection<? extends E> c) {
q = new PriorityQueue<E>(c);
}
-
- // these first few override just to update doc comments
-
/**
- * Adds the specified element to this queue.
- * @param o the element to add
- * @return <tt>true</tt> (as per the general contract of
- * <tt>Collection.add</tt>).
+ * Inserts the specified element into this priority queue.
*
- * @throws NullPointerException if the specified element is <tt>null</tt>.
+ * @param e the element to add
+ * @return <tt>true</tt> (as specified by {@link Collection#add})
* @throws ClassCastException if the specified element cannot be compared
- * with elements currently in the priority queue according
- * to the priority queue's ordering.
+ * with elements currently in the priority queue according to the
+ * priority queue's ordering
+ * @throws NullPointerException if the specified element is null
*/
- public boolean add(E o) {
- return super.add(o);
+ public boolean add(E e) {
+ return offer(e);
}
// BEGIN android-changed
/**
- * Returns the comparator used to order this collection, or <tt>null</tt>
- * if this collection is sorted according to its elements natural ordering
- * (using <tt>Comparable</tt>).
- *
- * @return the comparator used to order this collection, or <tt>null</tt>
- * if this collection is sorted according to its elements natural ordering.
- */
- public Comparator<? super E> comparator() {
- return q.comparator();
- }
- // END android-changed
-
- /**
* Inserts the specified element into this priority queue.
*
- * @param o the element to add
- * @return <tt>true</tt>
+ * @param e the element to add
+ * @return <tt>true</tt> (as specified by {@link Queue#offer})
* @throws ClassCastException if the specified element cannot be compared
- * with elements currently in the priority queue according
- * to the priority queue's ordering.
- * @throws NullPointerException if the specified element is <tt>null</tt>.
+ * with elements currently in the priority queue according to the
+ * priority queue's ordering
+ * @throws NullPointerException if the specified element is null
*/
- public boolean offer(E o) {
- if (o == null) throw new NullPointerException();
+ public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
- boolean ok = q.offer(o);
+ boolean ok = q.offer(e);
assert ok;
notEmpty.signal();
return true;
@@ -163,32 +171,44 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
}
/**
- * Adds the specified element to this priority queue. As the queue is
+ * Inserts the specified element into this priority queue. As the queue is
* unbounded this method will never block.
- * @param o the element to add
- * @throws ClassCastException if the element cannot be compared
- * with elements currently in the priority queue according
- * to the priority queue's ordering.
- * @throws NullPointerException if the specified element is <tt>null</tt>.
+ *
+ * @param e the element to add
+ * @throws ClassCastException if the specified element cannot be compared
+ * with elements currently in the priority queue according to the
+ * priority queue's ordering
+ * @throws NullPointerException if the specified element is null
*/
- public void put(E o) {
- offer(o); // never need to block
+ public void put(E e) {
+ offer(e); // never need to block
}
/**
* Inserts the specified element into this priority queue. As the queue is
* unbounded this method will never block.
- * @param o the element to add
+ *
+ * @param e the element to add
* @param timeout This parameter is ignored as the method never blocks
* @param unit This parameter is ignored as the method never blocks
* @return <tt>true</tt>
- * @throws ClassCastException if the element cannot be compared
- * with elements currently in the priority queue according
- * to the priority queue's ordering.
- * @throws NullPointerException if the specified element is <tt>null</tt>.
+ * @throws ClassCastException if the specified element cannot be compared
+ * with elements currently in the priority queue according to the
+ * priority queue's ordering
+ * @throws NullPointerException if the specified element is null
*/
- public boolean offer(E o, long timeout, TimeUnit unit) {
- return offer(o); // never need to block
+ public boolean offer(E e, long timeout, TimeUnit unit) {
+ return offer(e); // never need to block
+ }
+
+ public E poll() {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ return q.poll();
+ } finally {
+ lock.unlock();
+ }
}
public E take() throws InterruptedException {
@@ -210,17 +230,6 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
}
}
-
- public E poll() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return q.poll();
- } finally {
- lock.unlock();
- }
- }
-
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
@@ -254,6 +263,19 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
}
}
+ /**
+ * Returns the comparator used to order the elements in this queue,
+ * or <tt>null</tt> if this queue uses the {@linkplain Comparable
+ * natural ordering} of its elements.
+ *
+ * @return the comparator used to order the elements in this queue,
+ * or <tt>null</tt> if this queue uses the natural
+ * ordering of its elements
+ */
+ public Comparator<? super E> comparator() {
+ return q.comparator();
+ }
+
public int size() {
final ReentrantLock lock = this.lock;
lock.lock();
@@ -273,6 +295,17 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
return Integer.MAX_VALUE;
}
+ /**
+ * Removes a single instance of the specified element from this queue,
+ * if it is present. More formally, removes an element {@code e} such
+ * that {@code o.equals(e)}, if this queue contains one or more such
+ * elements. Returns {@code true} if and only 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) {
final ReentrantLock lock = this.lock;
lock.lock();
@@ -283,6 +316,14 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
}
}
+ /**
+ * Returns {@code true} if this queue contains the specified element.
+ * More formally, returns {@code true} if and only if this queue contains
+ * at least one element {@code e} such that {@code o.equals(e)}.
+ *
+ * @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) {
final ReentrantLock lock = this.lock;
lock.lock();
@@ -293,6 +334,19 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
}
}
+ /**
+ * Returns an array containing all of the elements in this queue.
+ * The returned array elements are in no particular order.
+ *
+ * <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 ReentrantLock lock = this.lock;
lock.lock();
@@ -314,6 +368,12 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
}
}
+ /**
+ * @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();
@@ -334,6 +394,12 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
}
}
+ /**
+ * @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();
@@ -357,7 +423,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
}
/**
- * Atomically removes all of the elements from this delay queue.
+ * Atomically removes all of the elements from this queue.
* The queue will be empty after this call returns.
*/
public void clear() {
@@ -370,6 +436,43 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
}
}
+ /**
+ * Returns an array containing all of the elements in this queue; the
+ * runtime type of the returned array is that of the specified array.
+ * The returned array elements are in no particular order.
+ * 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 ReentrantLock lock = this.lock;
lock.lock();
@@ -383,54 +486,58 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
/**
* Returns an iterator over the elements in this queue. The
* iterator does not return the elements in any particular order.
- * The returned iterator is a thread-safe "fast-fail" iterator
- * that will throw {@link
- * java.util.ConcurrentModificationException} upon detected
- * interference.
+ * 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.
+ * @return an iterator over the elements in this queue
*/
public Iterator<E> iterator() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return new Itr(q.iterator());
- } finally {
- lock.unlock();
- }
+ return new Itr(toArray());
}
- private class Itr<E> implements Iterator<E> {
- private final Iterator<E> iter;
- Itr(Iterator<E> i) {
- iter = i;
+ /**
+ * Snapshot iterator that works off copy of underlying q array.
+ */
+ private class Itr implements Iterator<E> {
+ final Object[] array; // Array of all elements
+ int cursor; // index of next element to return;
+ int lastRet; // index of last element, or -1 if no such
+
+ Itr(Object[] array) {
+ lastRet = -1;
+ this.array = array;
}
public boolean hasNext() {
- /*
- * No sync -- we rely on underlying hasNext to be
- * stateless, in which case we can return true by mistake
- * only when next() will subsequently throw
- * ConcurrentModificationException.
- */
- return iter.hasNext();
+ return cursor < array.length;
}
public E next() {
- ReentrantLock lock = PriorityBlockingQueue.this.lock;
- lock.lock();
- try {
- return iter.next();
- } finally {
- lock.unlock();
- }
+ if (cursor >= array.length)
+ throw new NoSuchElementException();
+ lastRet = cursor;
+ return (E)array[cursor++];
}
public void remove() {
- ReentrantLock lock = PriorityBlockingQueue.this.lock;
+ if (lastRet < 0)
+ throw new IllegalStateException();
+ Object x = array[lastRet];
+ lastRet = -1;
+ // Traverse underlying queue to find == element,
+ // not just a .equals element.
lock.lock();
try {
- iter.remove();
+ for (Iterator it = q.iterator(); it.hasNext(); ) {
+ if (it.next() == x) {
+ it.remove();
+ return;
+ }
+ }
} finally {
lock.unlock();
}
@@ -438,7 +545,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
}
/**
- * Save the state to a stream (that is, serialize it). This
+ * Saves the state to a stream (that is, serializes it). This
* merely wraps default serialization within lock. The
* serialization strategy for items is left to underlying
* Queue. Note that locking is not needed on deserialization, so
diff --git a/concurrent/src/main/java/java/util/concurrent/RejectedExecutionException.java b/concurrent/src/main/java/java/util/concurrent/RejectedExecutionException.java
index 199b9de..30b043d 100644
--- a/concurrent/src/main/java/java/util/concurrent/RejectedExecutionException.java
+++ b/concurrent/src/main/java/java/util/concurrent/RejectedExecutionException.java
@@ -9,7 +9,7 @@ package java.util.concurrent;
/**
* Exception thrown by an {@link Executor} when a task cannot be
* accepted for execution.
- *
+ *
* @since 1.5
* @author Doug Lea
*/
diff --git a/concurrent/src/main/java/java/util/concurrent/RejectedExecutionHandler.java b/concurrent/src/main/java/java/util/concurrent/RejectedExecutionHandler.java
index 4b4bbea..417a27c 100644
--- a/concurrent/src/main/java/java/util/concurrent/RejectedExecutionHandler.java
+++ b/concurrent/src/main/java/java/util/concurrent/RejectedExecutionHandler.java
@@ -7,8 +7,7 @@
package java.util.concurrent;
/**
- * A handler for tasks that cannot be executed by a {@link
- * ThreadPoolExecutor}.
+ * A handler for tasks that cannot be executed by a {@link ThreadPoolExecutor}.
*
* @since 1.5
* @author Doug Lea
@@ -17,13 +16,14 @@ public interface RejectedExecutionHandler {
/**
* Method that may be invoked by a {@link ThreadPoolExecutor} when
- * <tt>execute</tt> cannot accept a task. This may occur when no
- * more threads or queue slots are available because their bounds
- * would be exceeded, or upon shutdown of the Executor.
+ * {@link ThreadPoolExecutor#execute execute} cannot accept a
+ * task. This may occur when no more threads or queue slots are
+ * available because their bounds would be exceeded, or upon
+ * shutdown of the Executor.
*
- * In the absence other alternatives, the method may throw an
- * unchecked {@link RejectedExecutionException}, which will be
- * propagated to the caller of <tt>execute</tt>.
+ * <p>In the absence of other alternatives, the method may throw
+ * an unchecked {@link RejectedExecutionException}, which will be
+ * propagated to the caller of {@code execute}.
*
* @param r the runnable task requested to be executed
* @param executor the executor attempting to execute this task
diff --git a/concurrent/src/main/java/java/util/concurrent/ScheduledExecutorService.java b/concurrent/src/main/java/java/util/concurrent/ScheduledExecutorService.java
index 9579fef..c170c4a 100644
--- a/concurrent/src/main/java/java/util/concurrent/ScheduledExecutorService.java
+++ b/concurrent/src/main/java/java/util/concurrent/ScheduledExecutorService.java
@@ -10,13 +10,13 @@ import java.util.*;
/**
* An {@link ExecutorService} that can schedule commands to run after a given
- * delay, or to execute periodically.
+ * delay, or to execute periodically.
*
* <p> The <tt>schedule</tt> methods create tasks with various delays
* and return a task object that can be used to cancel or check
* execution. The <tt>scheduleAtFixedRate</tt> and
* <tt>scheduleWithFixedDelay</tt> methods create and execute tasks
- * that run periodically until cancelled.
+ * that run periodically until cancelled.
*
* <p> Commands submitted using the {@link Executor#execute} and
* {@link ExecutorService} <tt>submit</tt> methods are scheduled with
@@ -33,27 +33,27 @@ import java.util.*;
* TimeUnit.MILLISECONDS)</tt>. Beware however that expiration of a
* relative delay need not coincide with the current <tt>Date</tt> at
* which the task is enabled due to network time synchronization
- * protocols, clock drift, or other factors.
+ * protocols, clock drift, or other factors.
*
* The {@link Executors} class provides convenient factory methods for
* the ScheduledExecutorService implementations provided in this package.
*
* <h3>Usage Example</h3>
- *
+ *
* Here is a class with a method that sets up a ScheduledExecutorService
* to beep every ten seconds for an hour:
*
* <pre>
- * import static java.util.concurrent.TimeUnit;
+ * import static java.util.concurrent.TimeUnit.*;
* class BeeperControl {
- * private final ScheduledExecutorService scheduler =
+ * private final ScheduledExecutorService scheduler =
* Executors.newScheduledThreadPool(1);
*
* public void beepForAnHour() {
* final Runnable beeper = new Runnable() {
* public void run() { System.out.println("beep"); }
* };
- * final ScheduledFuture&lt;?&gt; beeperHandle =
+ * final ScheduledFuture&lt;?&gt; beeperHandle =
* scheduler.scheduleAtFixedRate(beeper, 10, 10, SECONDS);
* scheduler.schedule(new Runnable() {
* public void run() { beeperHandle.cancel(true); }
@@ -70,76 +70,90 @@ public interface ScheduledExecutorService extends ExecutorService {
/**
* Creates and executes a one-shot action that becomes enabled
* after the given delay.
- * @param command the task to execute.
- * @param delay the time from now to delay execution.
- * @param unit the time unit of the delay parameter.
- * @return a Future representing pending completion of the task,
- * and whose <tt>get()</tt> method will return <tt>null</tt>
- * upon completion.
- * @throws RejectedExecutionException if task cannot be scheduled
- * for execution.
+ *
+ * @param command the task to execute
+ * @param delay the time from now to delay execution
+ * @param unit the time unit of the delay parameter
+ * @return a ScheduledFuture representing pending completion of
+ * the task and whose <tt>get()</tt> method will return
+ * <tt>null</tt> upon completion
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
* @throws NullPointerException if command is null
*/
- public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
+ public ScheduledFuture<?> schedule(Runnable command,
+ long delay, TimeUnit unit);
/**
* Creates and executes a ScheduledFuture that becomes enabled after the
* given delay.
- * @param callable the function to execute.
- * @param delay the time from now to delay execution.
- * @param unit the time unit of the delay parameter.
- * @return a ScheduledFuture that can be used to extract result or cancel.
- * @throws RejectedExecutionException if task cannot be scheduled
- * for execution.
+ *
+ * @param callable the function to execute
+ * @param delay the time from now to delay execution
+ * @param unit the time unit of the delay parameter
+ * @return a ScheduledFuture that can be used to extract result or cancel
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
* @throws NullPointerException if callable is null
*/
- public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);
+ public <V> ScheduledFuture<V> schedule(Callable<V> callable,
+ long delay, TimeUnit unit);
/**
* Creates and executes a periodic action that becomes enabled first
* after the given initial delay, and subsequently with the given
* period; that is executions will commence after
* <tt>initialDelay</tt> then <tt>initialDelay+period</tt>, then
- * <tt>initialDelay + 2 * period</tt>, and so on.
+ * <tt>initialDelay + 2 * period</tt>, and so on.
* If any execution of the task
* encounters an exception, subsequent executions are suppressed.
* Otherwise, the task will only terminate via cancellation or
- * termination of the executor.
- * @param command the task to execute.
- * @param initialDelay the time to delay first execution.
- * @param period the period between successive executions.
+ * termination of the executor. If any execution of this task
+ * takes longer than its period, then subsequent executions
+ * may start late, but will not concurrently execute.
+ *
+ * @param command the task to execute
+ * @param initialDelay the time to delay first execution
+ * @param period the period between successive executions
* @param unit the time unit of the initialDelay and period parameters
- * @return a Future representing pending completion of the task,
- * and whose <tt>get()</tt> method will throw an exception upon
- * cancellation.
- * @throws RejectedExecutionException if task cannot be scheduled
- * for execution.
+ * @return a ScheduledFuture representing pending completion of
+ * the task, and whose <tt>get()</tt> method will throw an
+ * exception upon cancellation
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
* @throws NullPointerException if command is null
- * @throws IllegalArgumentException if period less than or equal to zero.
+ * @throws IllegalArgumentException if period less than or equal to zero
*/
- public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
-
+ public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
+ long initialDelay,
+ long period,
+ TimeUnit unit);
+
/**
* Creates and executes a periodic action that becomes enabled first
* after the given initial delay, and subsequently with the
* given delay between the termination of one execution and the
- * commencement of the next. If any execution of the task
+ * commencement of the next. If any execution of the task
* encounters an exception, subsequent executions are suppressed.
* Otherwise, the task will only terminate via cancellation or
* termination of the executor.
- * @param command the task to execute.
- * @param initialDelay the time to delay first execution.
+ *
+ * @param command the task to execute
+ * @param initialDelay the time to delay first execution
* @param delay the delay between the termination of one
- * execution and the commencement of the next.
+ * execution and the commencement of the next
* @param unit the time unit of the initialDelay and delay parameters
- * @return a Future representing pending completion of the task,
- * and whose <tt>get()</tt> method will throw an exception upon
- * cancellation.
- * @throws RejectedExecutionException if task cannot be scheduled
- * for execution.
+ * @return a ScheduledFuture representing pending completion of
+ * the task, and whose <tt>get()</tt> method will throw an
+ * exception upon cancellation
+ * @throws RejectedExecutionException if the task cannot be
+ * scheduled for execution
* @throws NullPointerException if command is null
- * @throws IllegalArgumentException if delay less than or equal to zero.
+ * @throws IllegalArgumentException if delay less than or equal to zero
*/
- public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);
+ public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
+ long initialDelay,
+ long delay,
+ TimeUnit unit);
}
diff --git a/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java b/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
index 09ec681..83ffb99 100644
--- a/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
+++ b/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
@@ -4,8 +4,13 @@
* http://creativecommons.org/licenses/publicdomain
*/
+/*
+ * Modified in Apache Harmony.
+ */
+
package java.util.concurrent;
import java.util.concurrent.atomic.*;
+import java.util.concurrent.locks.*;
import java.util.*;
/**
@@ -16,25 +21,60 @@ import java.util.*;
* flexibility or capabilities of {@link ThreadPoolExecutor} (which
* this class extends) are required.
*
- * <p> Delayed tasks execute no sooner than they are enabled, but
+ * <p>Delayed tasks execute no sooner than they are enabled, but
* without any real-time guarantees about when, after they are
* enabled, they will commence. Tasks scheduled for exactly the same
* execution time are enabled in first-in-first-out (FIFO) order of
* submission.
*
+ * <p>Successive executions of a task scheduled via
+ * <code>scheduleAtFixedRate</code> or
+ * <code>scheduleWithFixedDelay</code> do not overlap. While different
+ * executions may be performed by different threads, the effects of
+ * prior executions <a
+ * href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
+ * those of subsequent ones.
+ *
* <p>While this class inherits from {@link ThreadPoolExecutor}, a few
* of the inherited tuning methods are not useful for it. In
- * particular, because it acts as a fixed-sized pool using
- * <tt>corePoolSize</tt> threads and an unbounded queue, adjustments
- * to <tt>maximumPoolSize</tt> have no useful effect.
+ * particular, because it acts as a fixed-sized pool using {@code
+ * corePoolSize} threads and an unbounded queue, adjustments to {@code
+ * maximumPoolSize} have no useful effect.
*
* @since 1.5
* @author Doug Lea
*/
-public class ScheduledThreadPoolExecutor
- extends ThreadPoolExecutor
+public class ScheduledThreadPoolExecutor
+ extends ThreadPoolExecutor
implements ScheduledExecutorService {
+ /*
+ * This class specializes ThreadPoolExecutor implementation by
+ *
+ * 1. Using a custom task type, ScheduledFutureTask for
+ * tasks, even those that don't require scheduling (i.e.,
+ * those submitted using ExecutorService execute, not
+ * ScheduledExecutorService methods) which are treated as
+ * delayed tasks with a delay of zero.
+ *
+ * 2. Using a custom queue (DelayedWorkQueue), a variant of
+ * unbounded DelayQueue. The lack of capacity constraint and
+ * the fact that corePoolSize and maximumPoolSize are
+ * effectively identical simplifies some execution mechanics
+ * (see delayedExecute) compared to ThreadPoolExecutor.
+ *
+ * 3. Supporting optional run-after-shutdown parameters, which
+ * leads to overrides of shutdown methods to remove and cancel
+ * tasks that should NOT be run after shutdown, as well as
+ * different recheck logic when task (re)submission overlaps
+ * with a shutdown.
+ *
+ * 4. Task decoration methods to allow interception and
+ * instrumentation, which are needed because subclasses cannot
+ * otherwise override submit methods to get this effect. These
+ * don't have any impact on pool control logic though.
+ */
+
/**
* False if should cancel/suppress periodic tasks on shutdown.
*/
@@ -46,28 +86,40 @@ public class ScheduledThreadPoolExecutor
private volatile boolean executeExistingDelayedTasksAfterShutdown = true;
/**
+ * True if ScheduledFutureTask.cancel should remove from queue
+ */
+ private volatile boolean removeOnCancel = false;
+
+ /**
* Sequence number to break scheduling ties, and in turn to
* guarantee FIFO order among tied entries.
*/
private static final AtomicLong sequencer = new AtomicLong(0);
- /** Base of nanosecond timings, to avoid wrapping */
- private static final long NANO_ORIGIN = System.nanoTime();
/**
- * Returns nanosecond time offset by origin
+ * Value of System.nanoTime upon static initialization. This is
+ * used as an offset by now() to avoid wraparound of time values
+ * that would make them appear negative.
*/
- final long now() {
- return System.nanoTime() - NANO_ORIGIN;
+ static final long initialNanoTime = System.nanoTime();
+
+ /**
+ * Returns current nanosecond time.
+ */
+ static long now() {
+ return System.nanoTime() - initialNanoTime;
}
- private class ScheduledFutureTask<V>
+ private class ScheduledFutureTask<V>
extends FutureTask<V> implements ScheduledFuture<V> {
-
+
/** Sequence number to break ties FIFO */
private final long sequenceNumber;
+
/** The time the task is enabled to execute in nanoTime units */
private long time;
+
/**
* Period in nanoseconds for repeating tasks. A positive
* value indicates fixed-rate execution. A negative value
@@ -76,8 +128,16 @@ public class ScheduledThreadPoolExecutor
*/
private final long period;
+ /** The actual task to be re-enqueued by reExecutePeriodic */
+ ScheduledFutureTask<V> outerTask = this;
+
/**
- * Creates a one-shot action with given nanoTime-based trigger time
+ * Index into delay queue, to support faster cancellation.
+ */
+ int heapIndex;
+
+ /**
+ * Creates a one-shot action with given nanoTime-based trigger time.
*/
ScheduledFutureTask(Runnable r, V result, long ns) {
super(r, result);
@@ -87,9 +147,9 @@ public class ScheduledThreadPoolExecutor
}
/**
- * Creates a periodic action with given nano time and period
+ * Creates a periodic action with given nano time and period.
*/
- ScheduledFutureTask(Runnable r, V result, long ns, long period) {
+ ScheduledFutureTask(Runnable r, V result, long ns, long period) {
super(r, result);
this.time = ns;
this.period = period;
@@ -97,7 +157,7 @@ public class ScheduledThreadPoolExecutor
}
/**
- * Creates a one-shot action with given nanoTime-based trigger
+ * Creates a one-shot action with given nanoTime-based trigger.
*/
ScheduledFutureTask(Callable<V> callable, long ns) {
super(callable);
@@ -107,122 +167,162 @@ public class ScheduledThreadPoolExecutor
}
public long getDelay(TimeUnit unit) {
- long d = unit.convert(time - now(), TimeUnit.NANOSECONDS);
- return d;
+ long d = time - now();
+ return d<=0? 0 : unit.convert(d, TimeUnit.NANOSECONDS);
}
public int compareTo(Delayed other) {
if (other == this) // compare zero ONLY if same object
return 0;
- ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;
- long diff = time - x.time;
- if (diff < 0)
- return -1;
- else if (diff > 0)
- return 1;
- else if (sequenceNumber < x.sequenceNumber)
- return -1;
- else
- return 1;
+ if (other instanceof ScheduledFutureTask) {
+ ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;
+ long diff = time - x.time;
+ if (diff < 0)
+ return -1;
+ else if (diff > 0)
+ return 1;
+ else if (sequenceNumber < x.sequenceNumber)
+ return -1;
+ else
+ return 1;
+ }
+ long d = (getDelay(TimeUnit.NANOSECONDS) -
+ other.getDelay(TimeUnit.NANOSECONDS));
+ return (d == 0) ? 0 : ((d < 0) ? -1 : 1);
}
/**
* Returns true if this is a periodic (not a one-shot) action.
+ *
* @return true if periodic
*/
- boolean isPeriodic() {
+ public boolean isPeriodic() {
return period != 0;
}
/**
- * Run a periodic task
+ * Sets the next time to run for a periodic task.
*/
- private void runPeriodic() {
- boolean ok = ScheduledFutureTask.super.runAndReset();
- boolean down = isShutdown();
- // Reschedule if not cancelled and not shutdown or policy allows
- if (ok && (!down ||
- (getContinueExistingPeriodicTasksAfterShutdownPolicy() &&
- !isTerminating()))) {
- long p = period;
- if (p > 0)
- time += p;
- else
- time = now() - p;
- ScheduledThreadPoolExecutor.super.getQueue().add(this);
- }
- // This might have been the final executed delayed
- // task. Wake up threads to check.
- else if (down)
- interruptIdleWorkers();
+ private void setNextRunTime() {
+ long p = period;
+ if (p > 0)
+ time += p;
+ else
+ time = now() - p;
+ }
+
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ boolean cancelled = super.cancel(mayInterruptIfRunning);
+ if (cancelled && removeOnCancel && heapIndex >= 0)
+ remove(this);
+ return cancelled;
}
/**
* Overrides FutureTask version so as to reset/requeue if periodic.
- */
+ */
public void run() {
- if (isPeriodic())
- runPeriodic();
- else
+ boolean periodic = isPeriodic();
+ if (!canRunInCurrentRunState(periodic))
+ cancel(false);
+ else if (!periodic)
ScheduledFutureTask.super.run();
+ else if (ScheduledFutureTask.super.runAndReset()) {
+ setNextRunTime();
+ reExecutePeriodic(outerTask);
+ }
+ }
+ }
+
+ /**
+ * Returns true if can run a task given current run state
+ * and run-after-shutdown parameters.
+ *
+ * @param periodic true if this task periodic, false if delayed
+ */
+ boolean canRunInCurrentRunState(boolean periodic) {
+ return isRunningOrShutdown(periodic ?
+ continueExistingPeriodicTasksAfterShutdown :
+ executeExistingDelayedTasksAfterShutdown);
+ }
+
+ /**
+ * Main execution method for delayed or periodic tasks. If pool
+ * is shut down, rejects the task. Otherwise adds task to queue
+ * and starts a thread, if necessary, to run it. (We cannot
+ * prestart the thread to run the task because the task (probably)
+ * shouldn't be run yet,) If the pool is shut down while the task
+ * is being added, cancel and remove it if required by state and
+ * run-after-shutdown parameters.
+ *
+ * @param task the task
+ */
+ private void delayedExecute(ScheduledFutureTask<?> task) {
+ if (isShutdown())
+ reject(task);
+ else {
+ super.getQueue().add(task);
+ if (isShutdown() &&
+ !canRunInCurrentRunState(task.isPeriodic()) &&
+ remove(task))
+ task.cancel(false);
+ else
+ prestartCoreThread();
}
}
/**
- * Specialized variant of ThreadPoolExecutor.execute for delayed tasks.
- */
- private void delayedExecute(Runnable command) {
- if (isShutdown()) {
- reject(command);
- return;
- }
- // Prestart a thread if necessary. We cannot prestart it
- // running the task because the task (probably) shouldn't be
- // run yet, so thread will just idle until delay elapses.
- if (getPoolSize() < getCorePoolSize())
- prestartCoreThread();
-
- super.getQueue().add(command);
+ * Requeues a periodic task unless current run state precludes it.
+ * Same idea as delayedExecute except drops task rather than rejecting.
+ *
+ * @param task the task
+ */
+ void reExecutePeriodic(ScheduledFutureTask<?> task) {
+ if (canRunInCurrentRunState(true)) {
+ super.getQueue().add(task);
+ if (!canRunInCurrentRunState(true) && remove(task))
+ task.cancel(false);
+ else
+ prestartCoreThread();
+ }
}
/**
- * Cancel and clear the queue of all tasks that should not be run
- * due to shutdown policy.
- */
- private void cancelUnwantedTasks() {
- boolean keepDelayed = getExecuteExistingDelayedTasksAfterShutdownPolicy();
- boolean keepPeriodic = getContinueExistingPeriodicTasksAfterShutdownPolicy();
- if (!keepDelayed && !keepPeriodic)
- super.getQueue().clear();
- else if (keepDelayed || keepPeriodic) {
- Object[] entries = super.getQueue().toArray();
- for (int i = 0; i < entries.length; ++i) {
- Object e = entries[i];
+ * Cancels and clears the queue of all tasks that should not be run
+ * due to shutdown policy. Invoked within super.shutdown.
+ */
+ @Override void onShutdown() {
+ BlockingQueue<Runnable> q = super.getQueue();
+ boolean keepDelayed =
+ getExecuteExistingDelayedTasksAfterShutdownPolicy();
+ boolean keepPeriodic =
+ getContinueExistingPeriodicTasksAfterShutdownPolicy();
+ if (!keepDelayed && !keepPeriodic)
+ q.clear();
+ else {
+ // Traverse snapshot to avoid iterator exceptions
+ for (Object e : q.toArray()) {
if (e instanceof ScheduledFutureTask) {
- ScheduledFutureTask<?> t = (ScheduledFutureTask<?>)e;
- if (t.isPeriodic()? !keepPeriodic : !keepDelayed)
- t.cancel(false);
+ ScheduledFutureTask<?> t =
+ (ScheduledFutureTask<?>)e;
+ if ((t.isPeriodic() ? !keepPeriodic : !keepDelayed) ||
+ t.isCancelled()) { // also remove if already cancelled
+ if (q.remove(t))
+ t.cancel(false);
+ }
}
}
- entries = null;
- purge();
}
- }
-
- public boolean remove(Runnable task) {
- if (!(task instanceof ScheduledFutureTask))
- return false;
- return getQueue().remove(task);
+ tryTerminate();
}
/**
- * Creates a new ScheduledThreadPoolExecutor with the given core
- * pool size.
- *
- * @param corePoolSize the number of threads to keep in the pool,
- * even if they are idle.
- * @throws IllegalArgumentException if corePoolSize less than or
- * equal to zero
+ * Creates a new {@code ScheduledThreadPoolExecutor} with the
+ * given core pool size.
+ *
+ * @param corePoolSize the number of threads to keep in the pool, even
+ * if they are idle
+ * @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
@@ -230,14 +330,15 @@ public class ScheduledThreadPoolExecutor
}
/**
- * Creates a new ScheduledThreadPoolExecutor with the given
- * initial parameters.
- *
- * @param corePoolSize the number of threads to keep in the pool,
- * even if they are idle.
+ * Creates a new {@code ScheduledThreadPoolExecutor} with the
+ * given initial parameters.
+ *
+ * @param corePoolSize the number of threads to keep in the pool, even
+ * if they are idle
* @param threadFactory the factory to use when the executor
- * creates a new thread.
- * @throws NullPointerException if threadFactory is null
+ * creates a new thread
+ * @throws IllegalArgumentException if {@code corePoolSize < 0}
+ * @throws NullPointerException if {@code threadFactory} is null
*/
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
@@ -248,12 +349,13 @@ public class ScheduledThreadPoolExecutor
/**
* Creates a new ScheduledThreadPoolExecutor with the given
* initial parameters.
- *
- * @param corePoolSize the number of threads to keep in the pool,
- * even if they are idle.
+ *
+ * @param corePoolSize the number of threads to keep in the pool, even
+ * if they are idle
* @param handler the handler to use when execution is blocked
- * because the thread bounds and queue capacities are reached.
- * @throws NullPointerException if handler is null
+ * because the thread bounds and queue capacities are reached
+ * @throws IllegalArgumentException if {@code corePoolSize < 0}
+ * @throws NullPointerException if {@code handler} is null
*/
public ScheduledThreadPoolExecutor(int corePoolSize,
RejectedExecutionHandler handler) {
@@ -264,14 +366,16 @@ public class ScheduledThreadPoolExecutor
/**
* Creates a new ScheduledThreadPoolExecutor with the given
* initial parameters.
- *
- * @param corePoolSize the number of threads to keep in the pool,
- * even if they are idle.
+ *
+ * @param corePoolSize the number of threads to keep in the pool, even
+ * if they are idle
* @param threadFactory the factory to use when the executor
- * creates a new thread.
+ * creates a new thread
* @param handler the handler to use when execution is blocked
- * because the thread bounds and queue capacities are reached.
- * @throws NullPointerException if threadFactory or handler is null
+ * because the thread bounds and queue capacities are reached
+ * @throws IllegalArgumentException if {@code corePoolSize < 0}
+ * @throws NullPointerException if {@code threadFactory} or
+ * {@code handler} is null
*/
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
@@ -280,128 +384,178 @@ public class ScheduledThreadPoolExecutor
new DelayedWorkQueue(), threadFactory, handler);
}
- public ScheduledFuture<?> schedule(Runnable command,
- long delay,
+ /**
+ * Returns the trigger time of a delayed action
+ */
+ private static long nextTriggerTime(long delay, TimeUnit unit) {
+ long triggerTime;
+ long now = now();
+ if (delay <= 0)
+ return now; // avoid negative trigger times
+ else if ((triggerTime = now + unit.toNanos(delay)) < 0)
+ return Long.MAX_VALUE; // avoid numerical overflow
+ else
+ return triggerTime;
+ }
+
+ /**
+ * @throws RejectedExecutionException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ */
+ public ScheduledFuture<?> schedule(Runnable command,
+ long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
- long triggerTime = now() + unit.toNanos(delay);
- ScheduledFutureTask<?> t =
- new ScheduledFutureTask<Boolean>(command, null, triggerTime);
+ long triggerTime = nextTriggerTime(delay, unit);
+ ScheduledFutureTask<?> t
+ = new ScheduledFutureTask<Void>(command, null, triggerTime);
delayedExecute(t);
return t;
}
- public <V> ScheduledFuture<V> schedule(Callable<V> callable,
- long delay,
+ /**
+ * @throws RejectedExecutionException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ */
+ public <V> ScheduledFuture<V> schedule(Callable<V> callable,
+ long delay,
TimeUnit unit) {
if (callable == null || unit == null)
throw new NullPointerException();
- if (delay < 0) delay = 0;
- long triggerTime = now() + unit.toNanos(delay);
- ScheduledFutureTask<V> t =
- new ScheduledFutureTask<V>(callable, triggerTime);
+ long triggerTime = nextTriggerTime(delay, unit);
+ ScheduledFutureTask<V> t
+ = new ScheduledFutureTask<V>(callable, triggerTime);
delayedExecute(t);
return t;
}
- public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
- long initialDelay,
- long period,
+ /**
+ * @throws RejectedExecutionException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
+ */
+ public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
+ long initialDelay,
+ long period,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (period <= 0)
throw new IllegalArgumentException();
if (initialDelay < 0) initialDelay = 0;
- long triggerTime = now() + unit.toNanos(initialDelay);
- ScheduledFutureTask<?> t =
- new ScheduledFutureTask<Object>(command,
- null,
- triggerTime,
- unit.toNanos(period));
- delayedExecute(t);
- return t;
+ long triggerTime = nextTriggerTime(initialDelay, unit);
+ ScheduledFutureTask<Void> sft =
+ new ScheduledFutureTask<Void>(command,
+ null,
+ triggerTime,
+ unit.toNanos(period));
+ sft.outerTask = sft;
+ delayedExecute(sft);
+ return sft;
}
-
- public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
- long initialDelay,
- long delay,
+
+ /**
+ * @throws RejectedExecutionException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
+ */
+ public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
+ long initialDelay,
+ long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (delay <= 0)
throw new IllegalArgumentException();
- if (initialDelay < 0) initialDelay = 0;
- long triggerTime = now() + unit.toNanos(initialDelay);
- ScheduledFutureTask<?> t =
- new ScheduledFutureTask<Boolean>(command,
- null,
- triggerTime,
- unit.toNanos(-delay));
- delayedExecute(t);
- return t;
+ long triggerTime = nextTriggerTime(initialDelay, unit);
+ ScheduledFutureTask<Void> sft =
+ new ScheduledFutureTask<Void>(command,
+ null,
+ triggerTime,
+ unit.toNanos(-delay));
+ sft.outerTask = sft;
+ delayedExecute(sft);
+ return sft;
}
-
/**
- * Execute command with zero required delay. This has effect
- * equivalent to <tt>schedule(command, 0, anyUnit)</tt>. Note
- * that inspections of the queue and of the list returned by
- * <tt>shutdownNow</tt> will access the zero-delayed
- * {@link ScheduledFuture}, not the <tt>command</tt> itself.
+ * Executes {@code command} with zero required delay.
+ * This has effect equivalent to
+ * {@link #schedule(Runnable,long,TimeUnit) schedule(command, 0, anyUnit)}.
+ * Note that inspections of the queue and of the list returned by
+ * {@code shutdownNow} will access the zero-delayed
+ * {@link ScheduledFuture}, not the {@code command} itself.
+ *
+ * <p>A consequence of the use of {@code ScheduledFuture} objects is
+ * that {@link ThreadPoolExecutor#afterExecute afterExecute} is always
+ * called with a null second {@code Throwable} argument, even if the
+ * {@code command} terminated abruptly. Instead, the {@code Throwable}
+ * thrown by such a task can be obtained via {@link Future#get}.
*
- * @param command the task to execute
* @throws RejectedExecutionException at discretion of
- * <tt>RejectedExecutionHandler</tt>, if task cannot be accepted
- * for execution because the executor has been shut down.
- * @throws NullPointerException if command is null
+ * {@code RejectedExecutionHandler}, if the task
+ * cannot be accepted for execution because the
+ * executor has been shut down
+ * @throws NullPointerException {@inheritDoc}
*/
public void execute(Runnable command) {
- if (command == null)
- throw new NullPointerException();
schedule(command, 0, TimeUnit.NANOSECONDS);
}
// Override AbstractExecutorService methods
+ /**
+ * @throws RejectedExecutionException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ */
public Future<?> submit(Runnable task) {
return schedule(task, 0, TimeUnit.NANOSECONDS);
}
+ /**
+ * @throws RejectedExecutionException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ */
public <T> Future<T> submit(Runnable task, T result) {
- return schedule(Executors.callable(task, result),
+ return schedule(Executors.callable(task, result),
0, TimeUnit.NANOSECONDS);
}
+ /**
+ * @throws RejectedExecutionException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ */
public <T> Future<T> submit(Callable<T> task) {
return schedule(task, 0, TimeUnit.NANOSECONDS);
}
/**
- * Set policy on whether to continue executing existing periodic
- * tasks even when this executor has been <tt>shutdown</tt>. In
- * this case, these tasks will only terminate upon
- * <tt>shutdownNow</tt>, or after setting the policy to
- * <tt>false</tt> when already shutdown. This value is by default
- * false.
- * @param value if true, continue after shutdown, else don't.
- * @see #getExecuteExistingDelayedTasksAfterShutdownPolicy
+ * Sets the policy on whether to continue executing existing
+ * periodic tasks even when this executor has been {@code shutdown}.
+ * In this case, these tasks will only terminate upon
+ * {@code shutdownNow} or after setting the policy to
+ * {@code false} when already shutdown.
+ * This value is by default {@code false}.
+ *
+ * @param value if {@code true}, continue after shutdown, else don't.
+ * @see #getContinueExistingPeriodicTasksAfterShutdownPolicy
*/
public void setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean value) {
continueExistingPeriodicTasksAfterShutdown = value;
if (!value && isShutdown())
- cancelUnwantedTasks();
+ onShutdown();
}
/**
- * Get the policy on whether to continue executing existing
- * periodic tasks even when this executor has been
- * <tt>shutdown</tt>. In this case, these tasks will only
- * terminate upon <tt>shutdownNow</tt> or after setting the policy
- * to <tt>false</tt> when already shutdown. This value is by
- * default false.
- * @return true if will continue after shutdown.
+ * Gets the policy on whether to continue executing existing
+ * periodic tasks even when this executor has been {@code shutdown}.
+ * In this case, these tasks will only terminate upon
+ * {@code shutdownNow} or after setting the policy to
+ * {@code false} when already shutdown.
+ * This value is by default {@code false}.
+ *
+ * @return {@code true} if will continue after shutdown
* @see #setContinueExistingPeriodicTasksAfterShutdownPolicy
*/
public boolean getContinueExistingPeriodicTasksAfterShutdownPolicy() {
@@ -409,66 +563,79 @@ public class ScheduledThreadPoolExecutor
}
/**
- * Set policy on whether to execute existing delayed
- * tasks even when this executor has been <tt>shutdown</tt>. In
- * this case, these tasks will only terminate upon
- * <tt>shutdownNow</tt>, or after setting the policy to
- * <tt>false</tt> when already shutdown. This value is by default
- * true.
- * @param value if true, execute after shutdown, else don't.
+ * Sets the policy on whether to execute existing delayed
+ * tasks even when this executor has been {@code shutdown}.
+ * In this case, these tasks will only terminate upon
+ * {@code shutdownNow}, or after setting the policy to
+ * {@code false} when already shutdown.
+ * This value is by default {@code true}.
+ *
+ * @param value if {@code true}, execute after shutdown, else don't.
* @see #getExecuteExistingDelayedTasksAfterShutdownPolicy
*/
public void setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean value) {
executeExistingDelayedTasksAfterShutdown = value;
if (!value && isShutdown())
- cancelUnwantedTasks();
+ onShutdown();
}
/**
- * Get policy on whether to execute existing delayed
- * tasks even when this executor has been <tt>shutdown</tt>. In
- * this case, these tasks will only terminate upon
- * <tt>shutdownNow</tt>, or after setting the policy to
- * <tt>false</tt> when already shutdown. This value is by default
- * true.
- * @return true if will execute after shutdown.
+ * Gets the policy on whether to execute existing delayed
+ * tasks even when this executor has been {@code shutdown}.
+ * In this case, these tasks will only terminate upon
+ * {@code shutdownNow}, or after setting the policy to
+ * {@code false} when already shutdown.
+ * This value is by default {@code true}.
+ *
+ * @return {@code true} if will execute after shutdown
* @see #setExecuteExistingDelayedTasksAfterShutdownPolicy
*/
public boolean getExecuteExistingDelayedTasksAfterShutdownPolicy() {
return executeExistingDelayedTasksAfterShutdown;
}
-
/**
* Initiates an orderly shutdown in which previously submitted
- * tasks are executed, but no new tasks will be accepted. If the
- * <tt>ExecuteExistingDelayedTasksAfterShutdownPolicy</tt> has
- * been set <tt>false</tt>, existing delayed tasks whose delays
- * have not yet elapsed are cancelled. And unless the
- * <tt>ContinueExistingPeriodicTasksAfterShutdownPolicy</tt> has
- * been set <tt>true</tt>, future executions of existing periodic
- * tasks will be cancelled.
+ * tasks are executed, but no new tasks will be accepted.
+ * Invocation has no additional effect if already shut down.
+ *
+ * <p>This method does not wait for previously submitted tasks to
+ * complete execution. Use {@link #awaitTermination awaitTermination}
+ * to do that.
+ *
+ * <p>If the {@code ExecuteExistingDelayedTasksAfterShutdownPolicy}
+ * has been set {@code false}, existing delayed tasks whose delays
+ * have not yet elapsed are cancelled. And unless the {@code
+ * ContinueExistingPeriodicTasksAfterShutdownPolicy} has been set
+ * {@code true}, future executions of existing periodic tasks will
+ * be cancelled.
+ *
+ * @throws SecurityException {@inheritDoc}
*/
public void shutdown() {
- cancelUnwantedTasks();
super.shutdown();
}
/**
* Attempts to stop all actively executing tasks, halts the
- * processing of waiting tasks, and returns a list of the tasks that were
- * awaiting execution.
- *
+ * processing of waiting tasks, and returns a list of the tasks
+ * that were awaiting execution.
+ *
+ * <p>This method does not wait for actively executing tasks to
+ * terminate. Use {@link #awaitTermination awaitTermination} to
+ * do that.
+ *
* <p>There are no guarantees beyond best-effort attempts to stop
* processing actively executing tasks. This implementation
- * cancels tasks via {@link Thread#interrupt}, so if any tasks mask or
- * fail to respond to interrupts, they may never terminate.
+ * cancels tasks via {@link Thread#interrupt}, so any task that
+ * fails to respond to interrupts may never terminate.
*
- * @return list of tasks that never commenced execution. Each
- * element of this list is a {@link ScheduledFuture},
- * including those tasks submitted using <tt>execute</tt>, which
- * are for scheduling purposes used as the basis of a zero-delay
- * <tt>ScheduledFuture</tt>.
+ * @return list of tasks that never commenced execution.
+ * Each element of this list is a {@link ScheduledFuture},
+ * including those tasks submitted using {@code execute},
+ * which are for scheduling purposes used as the basis of a
+ * zero-delay {@code ScheduledFuture}.
+ * @throws SecurityException {@inheritDoc}
*/
public List<Runnable> shutdownNow() {
return super.shutdownNow();
@@ -477,9 +644,9 @@ public class ScheduledThreadPoolExecutor
/**
* Returns the task queue used by this executor. Each element of
* this queue is a {@link ScheduledFuture}, including those
- * tasks submitted using <tt>execute</tt> which are for scheduling
+ * tasks submitted using {@code execute} which are for scheduling
* purposes used as the basis of a zero-delay
- * <tt>ScheduledFuture</tt>. Iteration over this queue is
+ * {@code ScheduledFuture}. Iteration over this queue is
* <em>not</em> guaranteed to traverse tasks in the order in
* which they will execute.
*
@@ -490,52 +657,478 @@ public class ScheduledThreadPoolExecutor
}
/**
- * An annoying wrapper class to convince generics compiler to
- * use a DelayQueue<ScheduledFutureTask> as a BlockingQueue<Runnable>
- */
- private static class DelayedWorkQueue
- extends AbstractCollection<Runnable>
+ * Specialized delay queue. To mesh with TPE declarations, this
+ * class must be declared as a BlockingQueue<Runnable> even though
+ * it can only hold RunnableScheduledFutures.
+ */
+ static class DelayedWorkQueue extends AbstractQueue<Runnable>
implements BlockingQueue<Runnable> {
-
- private final DelayQueue<ScheduledFutureTask> dq = new DelayQueue<ScheduledFutureTask>();
- public Runnable poll() { return dq.poll(); }
- public Runnable peek() { return dq.peek(); }
- public Runnable take() throws InterruptedException { return dq.take(); }
- public Runnable poll(long timeout, TimeUnit unit) throws InterruptedException {
- return dq.poll(timeout, unit);
- }
-
- public boolean add(Runnable x) { return dq.add((ScheduledFutureTask)x); }
- public boolean offer(Runnable x) { return dq.offer((ScheduledFutureTask)x); }
- public void put(Runnable x) {
- dq.put((ScheduledFutureTask)x);
- }
- public boolean offer(Runnable x, long timeout, TimeUnit unit) {
- return dq.offer((ScheduledFutureTask)x, timeout, unit);
- }
-
- public Runnable remove() { return dq.remove(); }
- public Runnable element() { return dq.element(); }
- public void clear() { dq.clear(); }
- public int drainTo(Collection<? super Runnable> c) { return dq.drainTo(c); }
- public int drainTo(Collection<? super Runnable> c, int maxElements) {
- return dq.drainTo(c, maxElements);
- }
-
- public int remainingCapacity() { return dq.remainingCapacity(); }
- public boolean remove(Object x) { return dq.remove(x); }
- public boolean contains(Object x) { return dq.contains(x); }
- public int size() { return dq.size(); }
- public boolean isEmpty() { return dq.isEmpty(); }
- public Object[] toArray() { return dq.toArray(); }
- public <T> T[] toArray(T[] array) { return dq.toArray(array); }
- public Iterator<Runnable> iterator() {
- return new Iterator<Runnable>() {
- private Iterator<ScheduledFutureTask> it = dq.iterator();
- public boolean hasNext() { return it.hasNext(); }
- public Runnable next() { return it.next(); }
- public void remove() { it.remove(); }
- };
+
+ /*
+ * A DelayedWorkQueue is based on a heap-based data structure
+ * like those in DelayQueue and PriorityQueue, except that
+ * every ScheduledFutureTask also records its index into the
+ * heap array. This eliminates the need to find a task upon
+ * cancellation, greatly speeding up removal (down from O(n)
+ * to O(log n)), and reducing garbage retention that would
+ * otherwise occur by waiting for the element to rise to top
+ * before clearing. But because the queue may also hold
+ * RunnableScheduledFutures that are not ScheduledFutureTasks,
+ * we are not guaranteed to have such indices available, in
+ * which case we fall back to linear search. (We expect that
+ * most tasks will not be decorated, and that the faster cases
+ * will be much more common.)
+ *
+ * All heap operations must record index changes -- mainly
+ * within siftUp and siftDown. Upon removal, a task's
+ * heapIndex is set to -1. Note that ScheduledFutureTasks can
+ * appear at most once in the queue (this need not be true for
+ * other kinds of tasks or work queues), so are uniquely
+ * identified by heapIndex.
+ */
+
+ private static final int INITIAL_CAPACITY = 16;
+ private ScheduledFutureTask[] queue =
+ new ScheduledFutureTask[INITIAL_CAPACITY];
+ private final ReentrantLock lock = new ReentrantLock();
+ private int size = 0;
+
+ /**
+ * Thread designated to wait for the task at the head of the
+ * queue. This variant of the Leader-Follower pattern
+ * (http://www.cs.wustl.edu/~schmidt/POSA/POSA2/) serves to
+ * minimize unnecessary timed waiting. When a thread becomes
+ * the leader, it waits only for the next delay to elapse, but
+ * other threads await indefinitely. The leader thread must
+ * signal some other thread before returning from take() or
+ * poll(...), unless some other thread becomes leader in the
+ * interim. Whenever the head of the queue is replaced with a
+ * task with an earlier expiration time, the leader field is
+ * invalidated by being reset to null, and some waiting
+ * thread, but not necessarily the current leader, is
+ * signalled. So waiting threads must be prepared to acquire
+ * and lose leadership while waiting.
+ */
+ private Thread leader = null;
+
+ /**
+ * Condition signalled when a newer task becomes available at the
+ * head of the queue or a new thread may need to become leader.
+ */
+ private final Condition available = lock.newCondition();
+
+ /**
+ * Set f's heapIndex if it is a ScheduledFutureTask.
+ */
+ private void setIndex(ScheduledFutureTask f, int idx) {
+ if (f instanceof ScheduledFutureTask)
+ ((ScheduledFutureTask)f).heapIndex = idx;
+ }
+
+ /**
+ * Sift element added at bottom up to its heap-ordered spot.
+ * Call only when holding lock.
+ */
+ private void siftUp(int k, ScheduledFutureTask key) {
+ while (k > 0) {
+ int parent = (k - 1) >>> 1;
+ ScheduledFutureTask e = queue[parent];
+ if (key.compareTo(e) >= 0)
+ break;
+ queue[k] = e;
+ setIndex(e, k);
+ k = parent;
+ }
+ queue[k] = key;
+ setIndex(key, k);
+ }
+
+ /**
+ * Sift element added at top down to its heap-ordered spot.
+ * Call only when holding lock.
+ */
+ private void siftDown(int k, ScheduledFutureTask key) {
+ int half = size >>> 1;
+ while (k < half) {
+ int child = (k << 1) + 1;
+ ScheduledFutureTask c = queue[child];
+ int right = child + 1;
+ if (right < size && c.compareTo(queue[right]) > 0)
+ c = queue[child = right];
+ if (key.compareTo(c) <= 0)
+ break;
+ queue[k] = c;
+ setIndex(c, k);
+ k = child;
+ }
+ queue[k] = key;
+ setIndex(key, k);
+ }
+
+ /**
+ * Resize the heap array. Call only when holding lock.
+ */
+ private void grow() {
+ int oldCapacity = queue.length;
+ int newCapacity = oldCapacity + (oldCapacity >> 1); // grow 50%
+ if (newCapacity < 0) // overflow
+ newCapacity = Integer.MAX_VALUE;
+ queue = Java6Arrays.copyOf(queue, newCapacity);
+ }
+
+ /**
+ * Find index of given object, or -1 if absent
+ */
+ private int indexOf(Object x) {
+ if (x != null) {
+ if (x instanceof ScheduledFutureTask) {
+ int i = ((ScheduledFutureTask) x).heapIndex;
+ // Sanity check; x could conceivably be a
+ // ScheduledFutureTask from some other pool.
+ if (i >= 0 && i < size && queue[i] == x)
+ return i;
+ } else {
+ for (int i = 0; i < size; i++)
+ if (x.equals(queue[i]))
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public boolean contains(Object x) {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ return indexOf(x) != -1;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public boolean remove(Object x) {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ int i = indexOf(x);
+ if (i < 0)
+ return false;
+
+ setIndex(queue[i], -1);
+ int s = --size;
+ ScheduledFutureTask replacement = queue[s];
+ queue[s] = null;
+ if (s != i) {
+ siftDown(i, replacement);
+ if (queue[i] == replacement)
+ siftUp(i, replacement);
+ }
+ return true;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public int size() {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ return size;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ public int remainingCapacity() {
+ return Integer.MAX_VALUE;
+ }
+
+ public ScheduledFutureTask peek() {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ return queue[0];
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public boolean offer(Runnable x) {
+ if (x == null)
+ throw new NullPointerException();
+ ScheduledFutureTask e = (ScheduledFutureTask)x;
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ int i = size;
+ if (i >= queue.length)
+ grow();
+ size = i + 1;
+ if (i == 0) {
+ queue[0] = e;
+ setIndex(e, 0);
+ } else {
+ siftUp(i, e);
+ }
+ if (queue[0] == e) {
+ leader = null;
+ available.signal();
+ }
+ } finally {
+ lock.unlock();
+ }
+ return true;
+ }
+
+ public void put(Runnable e) {
+ offer(e);
+ }
+
+ public boolean add(Runnable e) {
+ return offer(e);
+ }
+
+ public boolean offer(Runnable e, long timeout, TimeUnit unit) {
+ return offer(e);
+ }
+
+ /**
+ * Performs common bookkeeping for poll and take: Replaces
+ * first element with last and sifts it down. Call only when
+ * holding lock.
+ * @param f the task to remove and return
+ */
+ private ScheduledFutureTask finishPoll(ScheduledFutureTask f) {
+ int s = --size;
+ ScheduledFutureTask x = queue[s];
+ queue[s] = null;
+ if (s != 0)
+ siftDown(0, x);
+ setIndex(f, -1);
+ return f;
+ }
+
+ public ScheduledFutureTask poll() {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ ScheduledFutureTask first = queue[0];
+ if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
+ return null;
+ else
+ return finishPoll(first);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public ScheduledFutureTask take() throws InterruptedException {
+ final ReentrantLock lock = this.lock;
+ lock.lockInterruptibly();
+ try {
+ for (;;) {
+ ScheduledFutureTask first = queue[0];
+ if (first == null)
+ available.await();
+ else {
+ long delay = first.getDelay(TimeUnit.NANOSECONDS);
+ if (delay <= 0)
+ return finishPoll(first);
+ else if (leader != null)
+ available.await();
+ else {
+ Thread thisThread = Thread.currentThread();
+ leader = thisThread;
+ try {
+ available.awaitNanos(delay);
+ } finally {
+ if (leader == thisThread)
+ leader = null;
+ }
+ }
+ }
+ }
+ } finally {
+ if (leader == null && queue[0] != null)
+ available.signal();
+ lock.unlock();
+ }
+ }
+
+ public ScheduledFutureTask poll(long timeout, TimeUnit unit)
+ throws InterruptedException {
+ long nanos = unit.toNanos(timeout);
+ final ReentrantLock lock = this.lock;
+ lock.lockInterruptibly();
+ try {
+ for (;;) {
+ ScheduledFutureTask first = queue[0];
+ if (first == null) {
+ if (nanos <= 0)
+ return null;
+ else
+ nanos = available.awaitNanos(nanos);
+ } else {
+ long delay = first.getDelay(TimeUnit.NANOSECONDS);
+ if (delay <= 0)
+ return finishPoll(first);
+ if (nanos <= 0)
+ return null;
+ if (nanos < delay || leader != null)
+ nanos = available.awaitNanos(nanos);
+ else {
+ Thread thisThread = Thread.currentThread();
+ leader = thisThread;
+ try {
+ long timeLeft = available.awaitNanos(delay);
+ nanos -= delay - timeLeft;
+ } finally {
+ if (leader == thisThread)
+ leader = null;
+ }
+ }
+ }
+ }
+ } finally {
+ if (leader == null && queue[0] != null)
+ available.signal();
+ lock.unlock();
+ }
+ }
+
+ public void clear() {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ for (int i = 0; i < size; i++) {
+ ScheduledFutureTask t = queue[i];
+ if (t != null) {
+ queue[i] = null;
+ setIndex(t, -1);
+ }
+ }
+ size = 0;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Return and remove first element only if it is expired.
+ * Used only by drainTo. Call only when holding lock.
+ */
+ private ScheduledFutureTask pollExpired() {
+ ScheduledFutureTask first = queue[0];
+ if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
+ return null;
+ return finishPoll(first);
+ }
+
+ public int drainTo(Collection<? super Runnable> c) {
+ if (c == null)
+ throw new NullPointerException();
+ if (c == this)
+ throw new IllegalArgumentException();
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ ScheduledFutureTask first;
+ int n = 0;
+ while ((first = pollExpired()) != null) {
+ c.add(first);
+ ++n;
+ }
+ return n;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public int drainTo(Collection<? super Runnable> c, int maxElements) {
+ if (c == null)
+ throw new NullPointerException();
+ if (c == this)
+ throw new IllegalArgumentException();
+ if (maxElements <= 0)
+ return 0;
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ ScheduledFutureTask first;
+ int n = 0;
+ while (n < maxElements && (first = pollExpired()) != null) {
+ c.add(first);
+ ++n;
+ }
+ return n;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public Object[] toArray() {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ return Java6Arrays.copyOf(queue, size, Object[].class);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T[] toArray(T[] a) {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ if (a.length < size)
+ return (T[]) Java6Arrays.copyOf(queue, size, a.getClass());
+ System.arraycopy(queue, 0, a, 0, size);
+ if (a.length > size)
+ a[size] = null;
+ return a;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public Iterator<Runnable> iterator() {
+ return new Itr(Java6Arrays.copyOf(queue, size));
+ }
+
+ /**
+ * Snapshot iterator that works off copy of underlying q array.
+ */
+ private class Itr implements Iterator<Runnable> {
+ final ScheduledFutureTask[] array;
+ int cursor = 0; // index of next element to return
+ int lastRet = -1; // index of last element, or -1 if no such
+
+ Itr(ScheduledFutureTask[] array) {
+ this.array = array;
+ }
+
+ public boolean hasNext() {
+ return cursor < array.length;
+ }
+
+ public Runnable next() {
+ if (cursor >= array.length)
+ throw new NoSuchElementException();
+ lastRet = cursor;
+ return array[cursor++];
+ }
+
+ public void remove() {
+ if (lastRet < 0)
+ throw new IllegalStateException();
+ DelayedWorkQueue.this.remove(array[lastRet]);
+ lastRet = -1;
+ }
}
}
}
diff --git a/concurrent/src/main/java/java/util/concurrent/Semaphore.java b/concurrent/src/main/java/java/util/concurrent/Semaphore.java
index 6eb80bc..1052364 100644
--- a/concurrent/src/main/java/java/util/concurrent/Semaphore.java
+++ b/concurrent/src/main/java/java/util/concurrent/Semaphore.java
@@ -14,7 +14,7 @@ import java.util.concurrent.atomic.*;
* permits. Each {@link #acquire} blocks if necessary until a permit is
* available, and then takes it. Each {@link #release} adds a permit,
* potentially releasing a blocking acquirer.
- * However, no actual permit objects are used; the <tt>Semaphore</tt> just
+ * However, no actual permit objects are used; the {@code Semaphore} just
* keeps a count of the number available and acts accordingly.
*
* <p>Semaphores are often used to restrict the number of threads than can
@@ -22,7 +22,7 @@ import java.util.concurrent.atomic.*;
* a class that uses a semaphore to control access to a pool of items:
* <pre>
* class Pool {
- * private static final MAX_AVAILABLE = 100;
+ * private static final int MAX_AVAILABLE = 100;
* private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
*
* public Object getItem() throws InterruptedException {
@@ -93,15 +93,19 @@ import java.util.concurrent.atomic.*;
* guarantees about the order in which threads acquire permits. In
* particular, <em>barging</em> is permitted, that is, a thread
* invoking {@link #acquire} can be allocated a permit ahead of a
- * thread that has been waiting. When fairness is set true, the
+ * thread that has been waiting - logically the new thread places itself at
+ * the head of the queue of waiting threads. When fairness is set true, the
* semaphore guarantees that threads invoking any of the {@link
- * #acquire() acquire} methods are allocated permits in the order in
+ * #acquire() acquire} methods are selected to obtain permits in the order in
* which their invocation of those methods was processed
* (first-in-first-out; FIFO). Note that FIFO ordering necessarily
* applies to specific internal points of execution within these
* methods. So, it is possible for one thread to invoke
- * <tt>acquire</tt> before another, but reach the ordering point after
+ * {@code acquire} before another, but reach the ordering point after
* the other, and similarly upon return from the method.
+ * Also note that the untimed {@link #tryAcquire() tryAcquire} methods do not
+ * honor the fairness setting, but will take any permits that are
+ * available.
*
* <p>Generally, semaphores used to control resource access should be
* initialized as fair, to ensure that no thread is starved out from
@@ -114,6 +118,12 @@ import java.util.concurrent.atomic.*;
* permits at a time. Beware of the increased risk of indefinite
* postponement when these methods are used without fairness set true.
*
+ * <p>Memory consistency effects: Actions in a thread prior to calling
+ * a "release" method such as {@code release()}
+ * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
+ * actions following a successful "acquire" method such as {@code acquire()}
+ * in another thread.
+ *
* @since 1.5
* @author Doug Lea
*
@@ -130,10 +140,12 @@ public class Semaphore implements java.io.Serializable {
* versions.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
+ private static final long serialVersionUID = 1192457210091910933L;
+
Sync(int permits) {
setState(permits);
}
-
+
final int getPermits() {
return getState();
}
@@ -147,11 +159,11 @@ public class Semaphore implements java.io.Serializable {
return remaining;
}
}
-
+
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int p = getState();
- if (compareAndSetState(p, p + releases))
+ if (compareAndSetState(p, p + releases))
return true;
}
}
@@ -178,10 +190,12 @@ public class Semaphore implements java.io.Serializable {
* NonFair version
*/
final static class NonfairSync extends Sync {
+ private static final long serialVersionUID = -2694183684443567898L;
+
NonfairSync(int permits) {
super(permits);
}
-
+
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
@@ -191,10 +205,12 @@ public class Semaphore implements java.io.Serializable {
* Fair version
*/
final static class FairSync extends Sync {
+ private static final long serialVersionUID = 2014338818796000944L;
+
FairSync(int permits) {
super(permits);
}
-
+
protected int tryAcquireShared(int acquires) {
Thread current = Thread.currentThread();
for (;;) {
@@ -211,57 +227,59 @@ public class Semaphore implements java.io.Serializable {
}
/**
- * Creates a <tt>Semaphore</tt> with the given number of
+ * Creates a {@code Semaphore} with the given number of
* permits and nonfair fairness setting.
- * @param permits the initial number of permits available. This
- * value may be negative, in which case releases must
- * occur before any acquires will be granted.
+ *
+ * @param permits the initial number of permits available.
+ * This value may be negative, in which case releases
+ * must occur before any acquires will be granted.
*/
- public Semaphore(int permits) {
+ public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
/**
- * Creates a <tt>Semaphore</tt> with the given number of
+ * Creates a {@code Semaphore} with the given number of
* permits and the given fairness setting.
- * @param permits the initial number of permits available. This
- * value may be negative, in which case releases must
- * occur before any acquires will be granted.
- * @param fair true if this semaphore will guarantee first-in
- * first-out granting of permits under contention, else false.
+ *
+ * @param permits the initial number of permits available.
+ * This value may be negative, in which case releases
+ * must occur before any acquires will be granted.
+ * @param fair {@code true} if this semaphore will guarantee
+ * first-in first-out granting of permits under contention,
+ * else {@code false}
*/
- public Semaphore(int permits, boolean fair) {
+ public Semaphore(int permits, boolean fair) {
sync = (fair)? new FairSync(permits) : new NonfairSync(permits);
}
/**
* Acquires a permit from this semaphore, blocking until one is
- * available, or the thread is {@link Thread#interrupt interrupted}.
+ * available, or the thread is {@linkplain Thread#interrupt interrupted}.
*
* <p>Acquires a permit, if one is available and returns immediately,
* reducing the number of available permits by one.
+ *
* <p>If no permit is available then the current thread becomes
* disabled for thread scheduling purposes and lies dormant until
* one of two things happens:
* <ul>
* <li>Some other thread invokes the {@link #release} method for this
* semaphore and the current thread is next to be assigned a permit; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread.
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
+ * the current thread.
* </ul>
*
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
- * <li>is {@link Thread#interrupt interrupted} while waiting
+ * <li>is {@linkplain Thread#interrupt interrupted} while waiting
* for a permit,
* </ul>
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* @throws InterruptedException if the current thread is interrupted
- *
- * @see Thread#interrupt
*/
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
@@ -273,103 +291,105 @@ public class Semaphore implements java.io.Serializable {
*
* <p>Acquires a permit, if one is available and returns immediately,
* reducing the number of available permits by one.
+ *
* <p>If no permit is available then the current thread becomes
* disabled for thread scheduling purposes and lies dormant until
* some other thread invokes the {@link #release} method for this
* semaphore and the current thread is next to be assigned a permit.
*
- * <p>If the current thread
- * is {@link Thread#interrupt interrupted} while waiting
- * for a permit then it will continue to wait, but the time at which
- * the thread is assigned a permit may change compared to the time it
- * would have received the permit had no interruption occurred. When the
- * thread does return from this method its interrupt status will be set.
- *
+ * <p>If the current thread is {@linkplain Thread#interrupt interrupted}
+ * while waiting for a permit then it will continue to wait, but the
+ * time at which the thread is assigned a permit may change compared to
+ * the time it would have received the permit had no interruption
+ * occurred. When the thread does return from this method its interrupt
+ * status will be set.
*/
public void acquireUninterruptibly() {
sync.acquireShared(1);
}
/**
- * Acquires a permit from this semaphore, only if one is available at the
+ * Acquires a permit from this semaphore, only if one is available at the
* time of invocation.
+ *
* <p>Acquires a permit, if one is available and returns immediately,
- * with the value <tt>true</tt>,
+ * with the value {@code true},
* reducing the number of available permits by one.
*
* <p>If no permit is available then this method will return
- * immediately with the value <tt>false</tt>.
+ * immediately with the value {@code false}.
*
* <p>Even when this semaphore has been set to use a
- * fair ordering policy, a call to <tt>tryAcquire()</tt> <em>will</em>
+ * fair ordering policy, a call to {@code tryAcquire()} <em>will</em>
* immediately acquire a permit if one is available, whether or not
- * other threads are currently waiting.
- * This &quot;barging&quot; behavior can be useful in certain
+ * other threads are currently waiting.
+ * This &quot;barging&quot; behavior can be useful in certain
* circumstances, even though it breaks fairness. If you want to honor
- * the fairness setting, then use
+ * the fairness setting, then use
* {@link #tryAcquire(long, TimeUnit) tryAcquire(0, TimeUnit.SECONDS) }
* which is almost equivalent (it also detects interruption).
*
- * @return <tt>true</tt> if a permit was acquired and <tt>false</tt>
- * otherwise.
+ * @return {@code true} if a permit was acquired and {@code false}
+ * otherwise
*/
public boolean tryAcquire() {
return sync.nonfairTryAcquireShared(1) >= 0;
}
/**
- * Acquires a permit from this semaphore, if one becomes available
- * within the given waiting time and the
- * current thread has not been {@link Thread#interrupt interrupted}.
+ * Acquires a permit from this semaphore, if one becomes available
+ * within the given waiting time and the current thread has not
+ * been {@linkplain Thread#interrupt interrupted}.
+ *
* <p>Acquires a permit, if one is available and returns immediately,
- * with the value <tt>true</tt>,
+ * with the value {@code true},
* reducing the number of available permits by one.
- * <p>If no permit is available then
- * the current thread becomes disabled for thread scheduling
- * purposes and lies dormant until one of three things happens:
+ *
+ * <p>If no permit is available then the current thread becomes
+ * disabled for thread scheduling purposes and lies dormant until
+ * one of three things happens:
* <ul>
* <li>Some other thread invokes the {@link #release} method for this
* semaphore and the current thread is next to be assigned a permit; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread; or
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
+ * the current thread; or
* <li>The specified waiting time elapses.
* </ul>
- * <p>If a permit is acquired then the value <tt>true</tt> is returned.
+ *
+ * <p>If a permit is acquired then the value {@code true} is returned.
+ *
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
- * <li>is {@link Thread#interrupt interrupted} while waiting to acquire
- * a permit,
+ * <li>is {@linkplain Thread#interrupt interrupted} while waiting
+ * to acquire a permit,
* </ul>
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
- * <p>If the specified waiting time elapses then the value <tt>false</tt>
- * is returned.
- * If the time is less than or equal to zero, the method will not wait
- * at all.
*
- * @param timeout the maximum time to wait for a permit
- * @param unit the time unit of the <tt>timeout</tt> argument.
- * @return <tt>true</tt> if a permit was acquired and <tt>false</tt>
- * if the waiting time elapsed before a permit was acquired.
+ * <p>If the specified waiting time elapses then the value {@code false}
+ * is returned. If the time is less than or equal to zero, the method
+ * will not wait at all.
*
+ * @param timeout the maximum time to wait for a permit
+ * @param unit the time unit of the {@code timeout} argument
+ * @return {@code true} if a permit was acquired and {@code false}
+ * if the waiting time elapsed before a permit was acquired
* @throws InterruptedException if the current thread is interrupted
- *
- * @see Thread#interrupt
- *
*/
- public boolean tryAcquire(long timeout, TimeUnit unit)
+ public boolean tryAcquire(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
/**
* Releases a permit, returning it to the semaphore.
- * <p>Releases a permit, increasing the number of available permits
- * by one.
- * If any threads are blocking trying to acquire a permit, then one
- * is selected and given the permit that was just released.
- * That thread is re-enabled for thread scheduling purposes.
+ *
+ * <p>Releases a permit, increasing the number of available permits by
+ * one. If any threads are trying to acquire a permit, then one is
+ * selected and given the permit that was just released. That thread
+ * is (re)enabled for thread scheduling purposes.
+ *
* <p>There is no requirement that a thread that releases a permit must
* have acquired that permit by calling {@link #acquire}.
* Correct usage of a semaphore is established by programming convention
@@ -378,45 +398,42 @@ public class Semaphore implements java.io.Serializable {
public void release() {
sync.releaseShared(1);
}
-
+
/**
- * Acquires the given number of permits from this semaphore,
- * blocking until all are available,
- * or the thread is {@link Thread#interrupt interrupted}.
+ * Acquires the given number of permits from this semaphore,
+ * blocking until all are available,
+ * or the thread is {@linkplain Thread#interrupt interrupted}.
*
* <p>Acquires the given number of permits, if they are available,
- * and returns immediately,
- * reducing the number of available permits by the given amount.
+ * and returns immediately, reducing the number of available permits
+ * by the given amount.
*
* <p>If insufficient permits are available then the current thread becomes
* disabled for thread scheduling purposes and lies dormant until
* one of two things happens:
* <ul>
- * <li>Some other thread invokes one of the {@link #release() release}
+ * <li>Some other thread invokes one of the {@link #release() release}
* methods for this semaphore, the current thread is next to be assigned
* permits and the number of available permits satisfies this request; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread.
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
+ * the current thread.
* </ul>
*
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
- * <li>is {@link Thread#interrupt interrupted} while waiting
+ * <li>is {@linkplain Thread#interrupt interrupted} while waiting
* for a permit,
* </ul>
* then {@link InterruptedException} is thrown and the current thread's
- * interrupted status is cleared.
- * Any permits that were to be assigned to this thread are instead
- * assigned to the next waiting thread(s), as if
- * they had been made available by a call to {@link #release()}.
+ * interrupted status is cleared.
+ * Any permits that were to be assigned to this thread are instead
+ * assigned to other threads trying to acquire permits, as if
+ * permits had been made available by a call to {@link #release()}.
*
* @param permits the number of permits to acquire
- *
* @throws InterruptedException if the current thread is interrupted
- * @throws IllegalArgumentException if permits less than zero.
- *
- * @see Thread#interrupt
+ * @throws IllegalArgumentException if {@code permits} is negative
*/
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
@@ -424,27 +441,26 @@ public class Semaphore implements java.io.Serializable {
}
/**
- * Acquires the given number of permits from this semaphore,
+ * Acquires the given number of permits from this semaphore,
* blocking until all are available.
*
* <p>Acquires the given number of permits, if they are available,
- * and returns immediately,
- * reducing the number of available permits by the given amount.
+ * and returns immediately, reducing the number of available permits
+ * by the given amount.
*
* <p>If insufficient permits are available then the current thread becomes
* disabled for thread scheduling purposes and lies dormant until
- * some other thread invokes one of the {@link #release() release}
+ * some other thread invokes one of the {@link #release() release}
* methods for this semaphore, the current thread is next to be assigned
* permits and the number of available permits satisfies this request.
*
- * <p>If the current thread
- * is {@link Thread#interrupt interrupted} while waiting
- * for permits then it will continue to wait and its position in the
- * queue is not affected. When the
- * thread does return from this method its interrupt status will be set.
+ * <p>If the current thread is {@linkplain Thread#interrupt interrupted}
+ * while waiting for permits then it will continue to wait and its
+ * position in the queue is not affected. When the thread does return
+ * from this method its interrupt status will be set.
*
* @param permits the number of permits to acquire
- * @throws IllegalArgumentException if permits less than zero.
+ * @throws IllegalArgumentException if {@code permits} is negative
*
*/
public void acquireUninterruptibly(int permits) {
@@ -456,16 +472,16 @@ public class Semaphore implements java.io.Serializable {
* Acquires the given number of permits from this semaphore, only
* if all are available at the time of invocation.
*
- * <p>Acquires the given number of permits, if they are available, and
- * returns immediately, with the value <tt>true</tt>,
+ * <p>Acquires the given number of permits, if they are available, and
+ * returns immediately, with the value {@code true},
* reducing the number of available permits by the given amount.
*
* <p>If insufficient permits are available then this method will return
- * immediately with the value <tt>false</tt> and the number of available
+ * immediately with the value {@code false} and the number of available
* permits is unchanged.
*
* <p>Even when this semaphore has been set to use a fair ordering
- * policy, a call to <tt>tryAcquire</tt> <em>will</em>
+ * policy, a call to {@code tryAcquire} <em>will</em>
* immediately acquire a permit if one is available, whether or
* not other threads are currently waiting. This
* &quot;barging&quot; behavior can be useful in certain
@@ -475,10 +491,9 @@ public class Semaphore implements java.io.Serializable {
* which is almost equivalent (it also detects interruption).
*
* @param permits the number of permits to acquire
- *
- * @return <tt>true</tt> if the permits were acquired and <tt>false</tt>
- * otherwise.
- * @throws IllegalArgumentException if permits less than zero.
+ * @return {@code true} if the permits were acquired and
+ * {@code false} otherwise
+ * @throws IllegalArgumentException if {@code permits} is negative
*/
public boolean tryAcquire(int permits) {
if (permits < 0) throw new IllegalArgumentException();
@@ -486,55 +501,54 @@ public class Semaphore implements java.io.Serializable {
}
/**
- * Acquires the given number of permits from this semaphore, if all
- * become available within the given waiting time and the
- * current thread has not been {@link Thread#interrupt interrupted}.
- * <p>Acquires the given number of permits, if they are available and
- * returns immediately, with the value <tt>true</tt>,
+ * Acquires the given number of permits from this semaphore, if all
+ * become available within the given waiting time and the current
+ * thread has not been {@linkplain Thread#interrupt interrupted}.
+ *
+ * <p>Acquires the given number of permits, if they are available and
+ * returns immediately, with the value {@code true},
* reducing the number of available permits by the given amount.
+ *
* <p>If insufficient permits are available then
* the current thread becomes disabled for thread scheduling
* purposes and lies dormant until one of three things happens:
* <ul>
- * <li>Some other thread invokes one of the {@link #release() release}
+ * <li>Some other thread invokes one of the {@link #release() release}
* methods for this semaphore, the current thread is next to be assigned
* permits and the number of available permits satisfies this request; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread; or
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
+ * the current thread; or
* <li>The specified waiting time elapses.
* </ul>
- * <p>If the permits are acquired then the value <tt>true</tt> is returned.
+ *
+ * <p>If the permits are acquired then the value {@code true} is returned.
+ *
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
- * <li>is {@link Thread#interrupt interrupted} while waiting to acquire
- * the permits,
+ * <li>is {@linkplain Thread#interrupt interrupted} while waiting
+ * to acquire the permits,
* </ul>
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
- * Any permits that were to be assigned to this thread, are instead
- * assigned to the next waiting thread(s), as if
- * they had been made available by a call to {@link #release()}.
- *
- * <p>If the specified waiting time elapses then the value <tt>false</tt>
- * is returned.
- * If the time is
- * less than or equal to zero, the method will not wait at all.
- * Any permits that were to be assigned to this thread, are instead
- * assigned to the next waiting thread(s), as if
- * they had been made available by a call to {@link #release()}.
+ * Any permits that were to be assigned to this thread, are instead
+ * assigned to other threads trying to acquire permits, as if
+ * the permits had been made available by a call to {@link #release()}.
+ *
+ * <p>If the specified waiting time elapses then the value {@code false}
+ * is returned. If the time is less than or equal to zero, the method
+ * will not wait at all. Any permits that were to be assigned to this
+ * thread, are instead assigned to other threads trying to acquire
+ * permits, as if the permits had been made available by a call to
+ * {@link #release()}.
*
* @param permits the number of permits to acquire
* @param timeout the maximum time to wait for the permits
- * @param unit the time unit of the <tt>timeout</tt> argument.
- * @return <tt>true</tt> if all permits were acquired and <tt>false</tt>
- * if the waiting time elapsed before all permits were acquired.
- *
+ * @param unit the time unit of the {@code timeout} argument
+ * @return {@code true} if all permits were acquired and {@code false}
+ * if the waiting time elapsed before all permits were acquired
* @throws InterruptedException if the current thread is interrupted
- * @throws IllegalArgumentException if permits less than zero.
- *
- * @see Thread#interrupt
- *
+ * @throws IllegalArgumentException if {@code permits} is negative
*/
public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
throws InterruptedException {
@@ -544,19 +558,17 @@ public class Semaphore implements java.io.Serializable {
/**
* Releases the given number of permits, returning them to the semaphore.
- * <p>Releases the given number of permits, increasing the number of
+ *
+ * <p>Releases the given number of permits, increasing the number of
* available permits by that amount.
- * If any threads are blocking trying to acquire permits, then the
- * one that has been waiting the longest
+ * If any threads are trying to acquire permits, then one
* is selected and given the permits that were just released.
* If the number of available permits satisfies that thread's request
- * then that thread is re-enabled for thread scheduling purposes; otherwise
- * the thread continues to wait. If there are still permits available
- * after the first thread's request has been satisfied, then those permits
- * are assigned to the next waiting thread. If it is satisfied then it is
- * re-enabled for thread scheduling purposes. This continues until there
- * are insufficient permits to satisfy the next waiting thread, or there
- * are no more waiting threads.
+ * then that thread is (re)enabled for thread scheduling purposes;
+ * otherwise the thread will wait until sufficient permits are available.
+ * If there are still permits available
+ * after this thread's request has been satisfied, then those permits
+ * are assigned in turn to other threads trying to acquire permits.
*
* <p>There is no requirement that a thread that releases a permit must
* have acquired that permit by calling {@link Semaphore#acquire acquire}.
@@ -564,7 +576,7 @@ public class Semaphore implements java.io.Serializable {
* in the application.
*
* @param permits the number of permits to release
- * @throws IllegalArgumentException if permits less than zero.
+ * @throws IllegalArgumentException if {@code permits} is negative
*/
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
@@ -573,16 +585,19 @@ public class Semaphore implements java.io.Serializable {
/**
* Returns the current number of permits available in this semaphore.
+ *
* <p>This method is typically used for debugging and testing purposes.
- * @return the number of permits available in this semaphore.
+ *
+ * @return the number of permits available in this semaphore
*/
public int availablePermits() {
return sync.getPermits();
}
/**
- * Acquire and return all permits that are immediately available.
- * @return the number of permits
+ * Acquires and returns all permits that are immediately available.
+ *
+ * @return the number of permits acquired
*/
public int drainPermits() {
return sync.drainPermits();
@@ -592,19 +607,21 @@ public class Semaphore implements java.io.Serializable {
* Shrinks the number of available permits by the indicated
* reduction. This method can be useful in subclasses that use
* semaphores to track resources that become unavailable. This
- * method differs from <tt>acquire</tt> in that it does not block
+ * method differs from {@code acquire} in that it does not block
* waiting for permits to become available.
+ *
* @param reduction the number of permits to remove
- * @throws IllegalArgumentException if reduction is negative
+ * @throws IllegalArgumentException if {@code reduction} is negative
*/
protected void reducePermits(int reduction) {
- if (reduction < 0) throw new IllegalArgumentException();
+ if (reduction < 0) throw new IllegalArgumentException();
sync.reducePermits(reduction);
}
/**
- * Returns true if this semaphore has fairness set true.
- * @return true if this semaphore has fairness set true.
+ * Returns {@code true} if this semaphore has fairness set true.
+ *
+ * @return {@code true} if this semaphore has fairness set true
*/
public boolean isFair() {
return sync instanceof FairSync;
@@ -612,25 +629,25 @@ public class Semaphore implements java.io.Serializable {
/**
* Queries whether any threads are waiting to acquire. Note that
- * because cancellations may occur at any time, a <tt>true</tt>
+ * because cancellations may occur at any time, a {@code true}
* return does not guarantee that any other thread will ever
* acquire. This method is designed primarily for use in
* monitoring of the system state.
*
- * @return true if there may be other threads waiting to acquire
- * the lock.
+ * @return {@code true} if there may be other threads waiting to
+ * acquire the lock
*/
- public final boolean hasQueuedThreads() {
+ public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
/**
- * Returns an estimate of the number of threads waiting to
- * acquire. The value is only an estimate because the number of
- * threads may change dynamically while this method traverses
- * internal data structures. This method is designed for use in
- * monitoring of the system state, not for synchronization
- * control.
+ * Returns an estimate of the number of threads waiting to acquire.
+ * The value is only an estimate because the number of threads may
+ * change dynamically while this method traverses internal data
+ * structures. This method is designed for use in monitoring of the
+ * system state, not for synchronization control.
+ *
* @return the estimated number of threads waiting for this lock
*/
public final int getQueueLength() {
@@ -638,13 +655,13 @@ public class Semaphore implements java.io.Serializable {
}
/**
- * Returns a collection containing threads that may be waiting to
- * acquire. Because the actual set of threads may change
- * dynamically while constructing this result, the returned
- * collection is only a best-effort estimate. The elements of the
- * returned collection are in no particular order. This method is
- * designed to facilitate construction of subclasses that provide
- * more extensive monitoring facilities.
+ * Returns a collection containing threads that may be waiting to acquire.
+ * Because the actual set of threads may change dynamically while
+ * constructing this result, the returned collection is only a best-effort
+ * estimate. The elements of the returned collection are in no particular
+ * order. This method is designed to facilitate construction of
+ * subclasses that provide more extensive monitoring facilities.
+ *
* @return the collection of threads
*/
protected Collection<Thread> getQueuedThreads() {
@@ -653,13 +670,12 @@ public class Semaphore implements java.io.Serializable {
/**
* Returns a string identifying this semaphore, as well as its state.
- * The state, in brackets, includes the String
- * &quot;Permits =&quot; followed by the number of permits.
- * @return a string identifying this semaphore, as well as its
- * state
+ * The state, in brackets, includes the String {@code "Permits ="}
+ * followed by the number of permits.
+ *
+ * @return a string identifying this semaphore, as well as its state
*/
public String toString() {
return super.toString() + "[Permits = " + sync.getPermits() + "]";
}
-
}
diff --git a/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java b/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java
index b8d3536..0d297ff 100644
--- a/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java
+++ b/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java
@@ -1,11 +1,13 @@
/*
- * Written by Doug Lea with assistance from members of JCP JSR-166
- * Expert Group and released to the public domain, as explained at
+ * Written by Doug Lea, Bill Scherer, and Michael Scott 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.concurrent.atomic.*;
import java.util.*;
// BEGIN android-note
@@ -13,20 +15,21 @@ import java.util.*;
// END android-note
/**
- * A {@linkplain BlockingQueue blocking queue} in which each
- * <tt>put</tt> must wait for a <tt>take</tt>, and vice versa. A
- * synchronous queue does not have any internal capacity, not even a
- * capacity of one. You cannot <tt>peek</tt> at a synchronous queue
- * because an element is only present when you try to take it; you
- * cannot add an element (using any method) unless another thread is
- * trying to remove it; you cannot iterate as there is nothing to
- * iterate. The <em>head</em> of the queue is the element that the
- * first queued thread is trying to add to the queue; if there are no
- * queued threads then no element is being added and the head is
- * <tt>null</tt>. For purposes of other <tt>Collection</tt> methods
- * (for example <tt>contains</tt>), a <tt>SynchronousQueue</tt> acts
- * as an empty collection. This queue does not permit <tt>null</tt>
- * elements.
+ * A {@linkplain BlockingQueue blocking queue} in which each insert
+ * operation must wait for a corresponding remove operation by another
+ * thread, and vice versa. A synchronous queue does not have any
+ * internal capacity, not even a capacity of one. You cannot
+ * <tt>peek</tt> at a synchronous queue because an element is only
+ * present when you try to remove it; you cannot insert an element
+ * (using any method) unless another thread is trying to remove it;
+ * you cannot iterate as there is nothing to iterate. The
+ * <em>head</em> of the queue is the element that the first queued
+ * inserting thread is trying to add to the queue; if there is no such
+ * queued thread then no element is available for removal and
+ * <tt>poll()</tt> will return <tt>null</tt>. For purposes of other
+ * <tt>Collection</tt> methods (for example <tt>contains</tt>), a
+ * <tt>SynchronousQueue</tt> acts as an empty collection. This queue
+ * does not permit <tt>null</tt> elements.
*
* <p>Synchronous queues are similar to rendezvous channels used in
* CSP and Ada. They are well suited for handoff designs, in which an
@@ -37,449 +40,841 @@ import java.util.*;
* <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.
+ * to <tt>true</tt> grants threads access in FIFO order.
*
- * <p>This class implements all of the <em>optional</em> methods
- * of the {@link Collection} and {@link Iterator} interfaces.
+ * <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
+ * @author Doug Lea and Bill Scherer and Michael Scott
* @param <E> the type of elements held in this collection
*/
public class SynchronousQueue<E> extends AbstractQueue<E>
- implements BlockingQueue<E>, java.io.Serializable {
+ implements BlockingQueue<E>, java.io.Serializable {
private static final long serialVersionUID = -3223113410248163686L;
/*
- This implementation divides actions into two cases for puts:
-
- * An arriving producer that does not already have a waiting consumer
- creates a node holding item, and then waits for a consumer to take it.
- * An arriving producer that does already have a waiting consumer fills
- the slot node created by the consumer, and notifies it to continue.
-
- And symmetrically, two for takes:
-
- * An arriving consumer that does not already have a waiting producer
- creates an empty slot node, and then waits for a producer to fill it.
- * An arriving consumer that does already have a waiting producer takes
- item from the node created by the producer, and notifies it to continue.
-
- When a put or take waiting for the actions of its counterpart
- aborts due to interruption or timeout, it marks the node
- it created as "CANCELLED", which causes its counterpart to retry
- the entire put or take sequence.
-
- This requires keeping two simple queues, waitingProducers and
- waitingConsumers. Each of these can be FIFO (preserves fairness)
- or LIFO (improves throughput).
- */
-
- /** Lock protecting both wait queues */
- private final ReentrantLock qlock;
- /** Queue holding waiting puts */
- private final WaitQueue waitingProducers;
- /** Queue holding waiting takes */
- private final WaitQueue waitingConsumers;
+ * This class implements extensions of the dual stack and dual
+ * queue algorithms described in "Nonblocking Concurrent Objects
+ * with Condition Synchronization", by W. N. Scherer III and
+ * M. L. Scott. 18th Annual Conf. on Distributed Computing,
+ * Oct. 2004 (see also
+ * http://www.cs.rochester.edu/u/scott/synchronization/pseudocode/duals.html).
+ * The (Lifo) stack is used for non-fair mode, and the (Fifo)
+ * queue for fair mode. The performance of the two is generally
+ * similar. Fifo usually supports higher throughput under
+ * contention but Lifo maintains higher thread locality in common
+ * applications.
+ *
+ * A dual queue (and similarly stack) is one that at any given
+ * time either holds "data" -- items provided by put operations,
+ * or "requests" -- slots representing take operations, or is
+ * empty. A call to "fulfill" (i.e., a call requesting an item
+ * from a queue holding data or vice versa) dequeues a
+ * complementary node. The most interesting feature of these
+ * queues is that any operation can figure out which mode the
+ * queue is in, and act accordingly without needing locks.
+ *
+ * Both the queue and stack extend abstract class Transferer
+ * defining the single method transfer that does a put or a
+ * take. These are unified into a single method because in dual
+ * data structures, the put and take operations are symmetrical,
+ * so nearly all code can be combined. The resulting transfer
+ * methods are on the long side, but are easier to follow than
+ * they would be if broken up into nearly-duplicated parts.
+ *
+ * The queue and stack data structures share many conceptual
+ * similarities but very few concrete details. For simplicity,
+ * they are kept distinct so that they can later evolve
+ * separately.
+ *
+ * The algorithms here differ from the versions in the above paper
+ * in extending them for use in synchronous queues, as well as
+ * dealing with cancellation. The main differences include:
+ *
+ * 1. The original algorithms used bit-marked pointers, but
+ * the ones here use mode bits in nodes, leading to a number
+ * of further adaptations.
+ * 2. SynchronousQueues must block threads waiting to become
+ * fulfilled.
+ * 3. Support for cancellation via timeout and interrupts,
+ * including cleaning out cancelled nodes/threads
+ * from lists to avoid garbage retention and memory depletion.
+ *
+ * Blocking is mainly accomplished using LockSupport park/unpark,
+ * except that nodes that appear to be the next ones to become
+ * fulfilled first spin a bit (on multiprocessors only). On very
+ * busy synchronous queues, spinning can dramatically improve
+ * throughput. And on less busy ones, the amount of spinning is
+ * small enough not to be noticeable.
+ *
+ * Cleaning is done in different ways in queues vs stacks. For
+ * queues, we can almost always remove a node immediately in O(1)
+ * time (modulo retries for consistency checks) when it is
+ * cancelled. But if it may be pinned as the current tail, it must
+ * wait until some subsequent cancellation. For stacks, we need a
+ * potentially O(n) traversal to be sure that we can remove the
+ * node, but this can run concurrently with other threads
+ * accessing the stack.
+ *
+ * While garbage collection takes care of most node reclamation
+ * issues that otherwise complicate nonblocking algorithms, care
+ * is taken to "forget" references to data, other nodes, and
+ * threads that might be held on to long-term by blocked
+ * threads. In cases where setting to null would otherwise
+ * conflict with main algorithms, this is done by changing a
+ * node's link to now point to the node itself. This doesn't arise
+ * much for Stack nodes (because blocked threads do not hang on to
+ * old head pointers), but references in Queue nodes must be
+ * aggressively forgotten to avoid reachability of everything any
+ * node has ever referred to since arrival.
+ */
/**
- * Creates a <tt>SynchronousQueue</tt> with nonfair access policy.
+ * Shared internal API for dual stacks and queues.
*/
- public SynchronousQueue() {
- this(false);
+ static abstract class Transferer {
+ /**
+ * Performs a put or take.
+ *
+ * @param e if non-null, the item to be handed to a consumer;
+ * if null, requests that transfer return an item
+ * offered by producer.
+ * @param timed if this operation should timeout
+ * @param nanos the timeout, in nanoseconds
+ * @return if non-null, the item provided or received; if null,
+ * the operation failed due to timeout or interrupt --
+ * the caller can distinguish which of these occurred
+ * by checking Thread.interrupted.
+ */
+ abstract Object transfer(Object e, boolean timed, long nanos);
}
+ /** The number of CPUs, for spin control */
+ static final int NCPUS = Runtime.getRuntime().availableProcessors();
+
/**
- * Creates a <tt>SynchronousQueue</tt> with specified fairness policy.
- * @param fair if true, threads contend in FIFO order for access;
- * otherwise the order is unspecified.
+ * The number of times to spin before blocking in timed waits.
+ * The value is empirically derived -- it works well across a
+ * variety of processors and OSes. Empirically, the best value
+ * seems not to vary with number of CPUs (beyond 2) so is just
+ * a constant.
*/
- public SynchronousQueue(boolean fair) {
- if (fair) {
- qlock = new ReentrantLock(true);
- waitingProducers = new FifoWaitQueue();
- waitingConsumers = new FifoWaitQueue();
- }
- else {
- qlock = new ReentrantLock();
- waitingProducers = new LifoWaitQueue();
- waitingConsumers = new LifoWaitQueue();
- }
- }
+ static final int maxTimedSpins = (NCPUS < 2)? 0 : 32;
/**
- * Queue to hold waiting puts/takes; specialized to Fifo/Lifo below.
- * These queues have all transient fields, but are serializable
- * in order to recover fairness settings when deserialized.
+ * The number of times to spin before blocking in untimed waits.
+ * This is greater than timed value because untimed waits spin
+ * faster since they don't need to check times on each spin.
*/
- static abstract class WaitQueue implements java.io.Serializable {
- /** Create, add, and return node for x */
- abstract Node enq(Object x);
- /** Remove and return node, or null if empty */
- abstract Node deq();
- }
+ static final int maxUntimedSpins = maxTimedSpins * 16;
/**
- * FIFO queue to hold waiting puts/takes.
+ * The number of nanoseconds for which it is faster to spin
+ * rather than to use timed park. A rough estimate suffices.
*/
- static final class FifoWaitQueue extends WaitQueue implements java.io.Serializable {
- private static final long serialVersionUID = -3623113410248163686L;
- private transient Node head;
- private transient Node last;
-
- Node enq(Object x) {
- Node p = new Node(x);
- if (last == null)
- last = head = p;
- else
- last = last.next = p;
- return p;
- }
+ static final long spinForTimeoutThreshold = 1000L;
+
+ /** Dual stack */
+ static final class TransferStack extends Transferer {
+ /*
+ * This extends Scherer-Scott dual stack algorithm, differing,
+ * among other ways, by using "covering" nodes rather than
+ * bit-marked pointers: Fulfilling operations push on marker
+ * nodes (with FULFILLING bit set in mode) to reserve a spot
+ * to match a waiting node.
+ */
- Node deq() {
- Node p = head;
- if (p != null) {
- if ((head = p.next) == null)
- last = null;
- p.next = null;
+ /* Modes for SNodes, ORed together in node fields */
+ /** Node represents an unfulfilled consumer */
+ static final int REQUEST = 0;
+ /** Node represents an unfulfilled producer */
+ static final int DATA = 1;
+ /** Node is fulfilling another unfulfilled DATA or REQUEST */
+ static final int FULFILLING = 2;
+
+ /** Return true if m has fulfilling bit set */
+ static boolean isFulfilling(int m) { return (m & FULFILLING) != 0; }
+
+ /** Node class for TransferStacks. */
+ static final class SNode {
+ volatile SNode next; // next node in stack
+ volatile SNode match; // the node matched to this
+ volatile Thread waiter; // to control park/unpark
+ Object item; // data; or null for REQUESTs
+ int mode;
+ // Note: item and mode fields don't need to be volatile
+ // since they are always written before, and read after,
+ // other volatile/atomic operations.
+
+ SNode(Object item) {
+ this.item = item;
}
- return p;
- }
- }
- /**
- * LIFO queue to hold waiting puts/takes.
- */
- static final class LifoWaitQueue extends WaitQueue implements java.io.Serializable {
- private static final long serialVersionUID = -3633113410248163686L;
- private transient Node head;
+ static final AtomicReferenceFieldUpdater<SNode, SNode>
+ nextUpdater = AtomicReferenceFieldUpdater.newUpdater
+ (SNode.class, SNode.class, "next");
- Node enq(Object x) {
- return head = new Node(x, head);
- }
+ boolean casNext(SNode cmp, SNode val) {
+ return (cmp == next &&
+ nextUpdater.compareAndSet(this, cmp, val));
+ }
+
+ static final AtomicReferenceFieldUpdater<SNode, SNode>
+ matchUpdater = AtomicReferenceFieldUpdater.newUpdater
+ (SNode.class, SNode.class, "match");
+
+ /**
+ * Tries to match node s to this node, if so, waking up thread.
+ * Fulfillers call tryMatch to identify their waiters.
+ * Waiters block until they have been matched.
+ *
+ * @param s the node to match
+ * @return true if successfully matched to s
+ */
+ boolean tryMatch(SNode s) {
+ if (match == null &&
+ matchUpdater.compareAndSet(this, null, s)) {
+ Thread w = waiter;
+ if (w != null) { // waiters need at most one unpark
+ waiter = null;
+ LockSupport.unpark(w);
+ }
+ return true;
+ }
+ return match == s;
+ }
+
+ /**
+ * Tries to cancel a wait by matching node to itself.
+ */
+ void tryCancel() {
+ matchUpdater.compareAndSet(this, null, this);
+ }
- Node deq() {
- Node p = head;
- if (p != null) {
- head = p.next;
- p.next = null;
+ boolean isCancelled() {
+ return match == this;
}
- return p;
}
- }
- /**
- * Nodes each maintain an item and handle waits and signals for
- * getting and setting it. The class extends
- * AbstractQueuedSynchronizer to manage blocking, using AQS state
- * 0 for waiting, 1 for ack, -1 for cancelled.
- */
- static final class Node extends AbstractQueuedSynchronizer {
- /** Synchronization state value representing that node acked */
- private static final int ACK = 1;
- /** Synchronization state value representing that node cancelled */
- private static final int CANCEL = -1;
+ /** The head (top) of the stack */
+ volatile SNode head;
- /** The item being transferred */
- Object item;
- /** Next node in wait queue */
- Node next;
+ static final AtomicReferenceFieldUpdater<TransferStack, SNode>
+ headUpdater = AtomicReferenceFieldUpdater.newUpdater
+ (TransferStack.class, SNode.class, "head");
- /** Creates a node with initial item */
- Node(Object x) { item = x; }
+ boolean casHead(SNode h, SNode nh) {
+ return h == head && headUpdater.compareAndSet(this, h, nh);
+ }
- /** Creates a node with initial item and next */
- Node(Object x, Node n) { item = x; next = n; }
+ /**
+ * Creates or resets fields of a node. Called only from transfer
+ * where the node to push on stack is lazily created and
+ * reused when possible to help reduce intervals between reads
+ * and CASes of head and to avoid surges of garbage when CASes
+ * to push nodes fail due to contention.
+ */
+ static SNode snode(SNode s, Object e, SNode next, int mode) {
+ if (s == null) s = new SNode(e);
+ s.mode = mode;
+ s.next = next;
+ return s;
+ }
/**
- * Implements AQS base acquire to succeed if not in WAITING state
+ * Puts or takes an item.
*/
- protected boolean tryAcquire(int ignore) {
- return getState() != 0;
+ Object transfer(Object e, boolean timed, long nanos) {
+ /*
+ * Basic algorithm is to loop trying one of three actions:
+ *
+ * 1. If apparently empty or already containing nodes of same
+ * mode, try to push node on stack and wait for a match,
+ * returning it, or null if cancelled.
+ *
+ * 2. If apparently containing node of complementary mode,
+ * try to push a fulfilling node on to stack, match
+ * with corresponding waiting node, pop both from
+ * stack, and return matched item. The matching or
+ * unlinking might not actually be necessary because of
+ * other threads performing action 3:
+ *
+ * 3. If top of stack already holds another fulfilling node,
+ * help it out by doing its match and/or pop
+ * operations, and then continue. The code for helping
+ * is essentially the same as for fulfilling, except
+ * that it doesn't return the item.
+ */
+
+ SNode s = null; // constructed/reused as needed
+ int mode = (e == null)? REQUEST : DATA;
+
+ for (;;) {
+ SNode h = head;
+ if (h == null || h.mode == mode) { // empty or same-mode
+ if (timed && nanos <= 0) { // can't wait
+ if (h != null && h.isCancelled())
+ casHead(h, h.next); // pop cancelled node
+ else
+ return null;
+ } else if (casHead(h, s = snode(s, e, h, mode))) {
+ SNode m = awaitFulfill(s, timed, nanos);
+ if (m == s) { // wait was cancelled
+ clean(s);
+ return null;
+ }
+ if ((h = head) != null && h.next == s)
+ casHead(h, s.next); // help s's fulfiller
+ return mode == REQUEST? m.item : s.item;
+ }
+ } else if (!isFulfilling(h.mode)) { // try to fulfill
+ if (h.isCancelled()) // already cancelled
+ casHead(h, h.next); // pop and retry
+ else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) {
+ for (;;) { // loop until matched or waiters disappear
+ SNode m = s.next; // m is s's match
+ if (m == null) { // all waiters are gone
+ casHead(s, null); // pop fulfill node
+ s = null; // use new node next time
+ break; // restart main loop
+ }
+ SNode mn = m.next;
+ if (m.tryMatch(s)) {
+ casHead(s, mn); // pop both s and m
+ return (mode == REQUEST)? m.item : s.item;
+ } else // lost match
+ s.casNext(m, mn); // help unlink
+ }
+ }
+ } else { // help a fulfiller
+ SNode m = h.next; // m is h's match
+ if (m == null) // waiter is gone
+ casHead(h, null); // pop fulfilling node
+ else {
+ SNode mn = m.next;
+ if (m.tryMatch(h)) // help match
+ casHead(h, mn); // pop both h and m
+ else // lost match
+ h.casNext(m, mn); // help unlink
+ }
+ }
+ }
}
/**
- * Implements AQS base release to signal if state changed
+ * Spins/blocks until node s is matched by a fulfill operation.
+ *
+ * @param s the waiting node
+ * @param timed true if timed wait
+ * @param nanos timeout value
+ * @return matched node, or s if cancelled
*/
- protected boolean tryRelease(int newState) {
- return compareAndSetState(0, newState);
+ SNode awaitFulfill(SNode s, boolean timed, long nanos) {
+ /*
+ * When a node/thread is about to block, it sets its waiter
+ * field and then rechecks state at least one more time
+ * before actually parking, thus covering race vs
+ * fulfiller noticing that waiter is non-null so should be
+ * woken.
+ *
+ * When invoked by nodes that appear at the point of call
+ * to be at the head of the stack, calls to park are
+ * preceded by spins to avoid blocking when producers and
+ * consumers are arriving very close in time. This can
+ * happen enough to bother only on multiprocessors.
+ *
+ * The order of checks for returning out of main loop
+ * reflects fact that interrupts have precedence over
+ * normal returns, which have precedence over
+ * timeouts. (So, on timeout, one last check for match is
+ * done before giving up.) Except that calls from untimed
+ * SynchronousQueue.{poll/offer} don't check interrupts
+ * and don't wait at all, so are trapped in transfer
+ * method rather than calling awaitFulfill.
+ */
+ long lastTime = (timed)? System.nanoTime() : 0;
+ Thread w = Thread.currentThread();
+ SNode h = head;
+ int spins = (shouldSpin(s)?
+ (timed? maxTimedSpins : maxUntimedSpins) : 0);
+ for (;;) {
+ if (w.isInterrupted())
+ s.tryCancel();
+ SNode m = s.match;
+ if (m != null)
+ return m;
+ if (timed) {
+ long now = System.nanoTime();
+ nanos -= now - lastTime;
+ lastTime = now;
+ if (nanos <= 0) {
+ s.tryCancel();
+ continue;
+ }
+ }
+ if (spins > 0)
+ spins = shouldSpin(s)? (spins-1) : 0;
+ else if (s.waiter == null)
+ s.waiter = w; // establish waiter so can park next iter
+ else if (!timed)
+ LockSupport.park();
+ else if (nanos > spinForTimeoutThreshold)
+ LockSupport.parkNanos(nanos);
+ }
}
/**
- * Takes item and nulls out field (for sake of GC)
+ * Returns true if node s is at head or there is an active
+ * fulfiller.
*/
- private Object extract() {
- Object x = item;
- item = null;
- return x;
+ boolean shouldSpin(SNode s) {
+ SNode h = head;
+ return (h == s || h == null || isFulfilling(h.mode));
}
/**
- * Tries to cancel on interrupt; if so rethrowing,
- * else setting interrupt state
+ * Unlinks s from the stack.
*/
- private void checkCancellationOnInterrupt(InterruptedException ie)
- throws InterruptedException {
- if (release(CANCEL))
- throw ie;
- Thread.currentThread().interrupt();
+ void clean(SNode s) {
+ s.item = null; // forget item
+ s.waiter = null; // forget thread
+
+ /*
+ * At worst we may need to traverse entire stack to unlink
+ * s. If there are multiple concurrent calls to clean, we
+ * might not see s if another thread has already removed
+ * it. But we can stop when we see any node known to
+ * follow s. We use s.next unless it too is cancelled, in
+ * which case we try the node one past. We don't check any
+ * further because we don't want to doubly traverse just to
+ * find sentinel.
+ */
+
+ SNode past = s.next;
+ if (past != null && past.isCancelled())
+ past = past.next;
+
+ // Absorb cancelled nodes at head
+ SNode p;
+ while ((p = head) != null && p != past && p.isCancelled())
+ casHead(p, p.next);
+
+ // Unsplice embedded nodes
+ while (p != null && p != past) {
+ SNode n = p.next;
+ if (n != null && n.isCancelled())
+ p.casNext(n, n.next);
+ else
+ p = n;
+ }
}
+ }
+
+ /** Dual Queue */
+ static final class TransferQueue extends Transferer {
+ /*
+ * This extends Scherer-Scott dual queue algorithm, differing,
+ * among other ways, by using modes within nodes rather than
+ * marked pointers. The algorithm is a little simpler than
+ * that for stacks because fulfillers do not need explicit
+ * nodes, and matching is done by CAS'ing QNode.item field
+ * from non-null to null (for put) or vice versa (for take).
+ */
+
+ /** Node class for TransferQueue. */
+ static final class QNode {
+ volatile QNode next; // next node in queue
+ volatile Object item; // CAS'ed to or from null
+ volatile Thread waiter; // to control park/unpark
+ final boolean isData;
+
+ QNode(Object item, boolean isData) {
+ this.item = item;
+ this.isData = isData;
+ }
+
+ static final AtomicReferenceFieldUpdater<QNode, QNode>
+ nextUpdater = AtomicReferenceFieldUpdater.newUpdater
+ (QNode.class, QNode.class, "next");
+
+ boolean casNext(QNode cmp, QNode val) {
+ return (next == cmp &&
+ nextUpdater.compareAndSet(this, cmp, val));
+ }
+
+ static final AtomicReferenceFieldUpdater<QNode, Object>
+ itemUpdater = AtomicReferenceFieldUpdater.newUpdater
+ (QNode.class, Object.class, "item");
+
+ boolean casItem(Object cmp, Object val) {
+ return (item == cmp &&
+ itemUpdater.compareAndSet(this, cmp, val));
+ }
+
+ /**
+ * Tries to cancel by CAS'ing ref to this as item.
+ */
+ void tryCancel(Object cmp) {
+ itemUpdater.compareAndSet(this, cmp, this);
+ }
+
+ boolean isCancelled() {
+ return item == this;
+ }
+
+ /**
+ * Returns true if this node is known to be off the queue
+ * because its next pointer has been forgotten due to
+ * an advanceHead operation.
+ */
+ boolean isOffList() {
+ return next == this;
+ }
+ }
+
+ /** Head of queue */
+ transient volatile QNode head;
+ /** Tail of queue */
+ transient volatile QNode tail;
+ /**
+ * Reference to a cancelled node that might not yet have been
+ * unlinked from queue because it was the last inserted node
+ * when it cancelled.
+ */
+ transient volatile QNode cleanMe;
+
+ TransferQueue() {
+ QNode h = new QNode(null, false); // initialize to dummy node.
+ head = h;
+ tail = h;
+ }
+
+ static final AtomicReferenceFieldUpdater<TransferQueue, QNode>
+ headUpdater = AtomicReferenceFieldUpdater.newUpdater
+ (TransferQueue.class, QNode.class, "head");
/**
- * Fills in the slot created by the consumer and signal consumer to
- * continue.
+ * Tries to cas nh as new head; if successful, unlink
+ * old head's next node to avoid garbage retention.
*/
- boolean setItem(Object x) {
- item = x; // can place in slot even if cancelled
- return release(ACK);
+ void advanceHead(QNode h, QNode nh) {
+ if (h == head && headUpdater.compareAndSet(this, h, nh))
+ h.next = h; // forget old next
}
+ static final AtomicReferenceFieldUpdater<TransferQueue, QNode>
+ tailUpdater = AtomicReferenceFieldUpdater.newUpdater
+ (TransferQueue.class, QNode.class, "tail");
+
/**
- * Removes item from slot created by producer and signal producer
- * to continue.
+ * Tries to cas nt as new tail.
*/
- Object getItem() {
- return (release(ACK))? extract() : null;
+ void advanceTail(QNode t, QNode nt) {
+ if (tail == t)
+ tailUpdater.compareAndSet(this, t, nt);
}
+ static final AtomicReferenceFieldUpdater<TransferQueue, QNode>
+ cleanMeUpdater = AtomicReferenceFieldUpdater.newUpdater
+ (TransferQueue.class, QNode.class, "cleanMe");
+
/**
- * Waits for a consumer to take item placed by producer.
+ * Tries to CAS cleanMe slot.
*/
- void waitForTake() throws InterruptedException {
- try {
- acquireInterruptibly(0);
- } catch (InterruptedException ie) {
- checkCancellationOnInterrupt(ie);
- }
+ boolean casCleanMe(QNode cmp, QNode val) {
+ return (cleanMe == cmp &&
+ cleanMeUpdater.compareAndSet(this, cmp, val));
}
/**
- * Waits for a producer to put item placed by consumer.
+ * Puts or takes an item.
*/
- Object waitForPut() throws InterruptedException {
- try {
- acquireInterruptibly(0);
- } catch (InterruptedException ie) {
- checkCancellationOnInterrupt(ie);
+ Object transfer(Object e, boolean timed, long nanos) {
+ /* Basic algorithm is to loop trying to take either of
+ * two actions:
+ *
+ * 1. If queue apparently empty or holding same-mode nodes,
+ * try to add node to queue of waiters, wait to be
+ * fulfilled (or cancelled) and return matching item.
+ *
+ * 2. If queue apparently contains waiting items, and this
+ * call is of complementary mode, try to fulfill by CAS'ing
+ * item field of waiting node and dequeuing it, and then
+ * returning matching item.
+ *
+ * In each case, along the way, check for and try to help
+ * advance head and tail on behalf of other stalled/slow
+ * threads.
+ *
+ * The loop starts off with a null check guarding against
+ * seeing uninitialized head or tail values. This never
+ * happens in current SynchronousQueue, but could if
+ * callers held non-volatile/final ref to the
+ * transferer. The check is here anyway because it places
+ * null checks at top of loop, which is usually faster
+ * than having them implicitly interspersed.
+ */
+
+ QNode s = null; // constructed/reused as needed
+ boolean isData = (e != null);
+
+ for (;;) {
+ QNode t = tail;
+ QNode h = head;
+ if (t == null || h == null) // saw uninitialized value
+ continue; // spin
+
+ if (h == t || t.isData == isData) { // empty or same-mode
+ QNode tn = t.next;
+ if (t != tail) // inconsistent read
+ continue;
+ if (tn != null) { // lagging tail
+ advanceTail(t, tn);
+ continue;
+ }
+ if (timed && nanos <= 0) // can't wait
+ return null;
+ if (s == null)
+ s = new QNode(e, isData);
+ if (!t.casNext(null, s)) // failed to link in
+ continue;
+
+ advanceTail(t, s); // swing tail and wait
+ Object x = awaitFulfill(s, e, timed, nanos);
+ if (x == s) { // wait was cancelled
+ clean(t, s);
+ return null;
+ }
+
+ if (!s.isOffList()) { // not already unlinked
+ advanceHead(t, s); // unlink if head
+ if (x != null) // and forget fields
+ s.item = s;
+ s.waiter = null;
+ }
+ return (x != null)? x : e;
+
+ } else { // complementary-mode
+ QNode m = h.next; // node to fulfill
+ if (t != tail || m == null || h != head)
+ continue; // inconsistent read
+
+ Object x = m.item;
+ if (isData == (x != null) || // m already fulfilled
+ x == m || // m cancelled
+ !m.casItem(x, e)) { // lost CAS
+ advanceHead(h, m); // dequeue and retry
+ continue;
+ }
+
+ advanceHead(h, m); // successfully fulfilled
+ LockSupport.unpark(m.waiter);
+ return (x != null)? x : e;
+ }
}
- return extract();
}
/**
- * Waits for a consumer to take item placed by producer or time out.
+ * Spins/blocks until node s is fulfilled.
+ *
+ * @param s the waiting node
+ * @param e the comparison value for checking match
+ * @param timed true if timed wait
+ * @param nanos timeout value
+ * @return matched item, or s if cancelled
*/
- boolean waitForTake(long nanos) throws InterruptedException {
- try {
- if (!tryAcquireNanos(0, nanos) &&
- release(CANCEL))
- return false;
- } catch (InterruptedException ie) {
- checkCancellationOnInterrupt(ie);
+ Object awaitFulfill(QNode s, Object e, boolean timed, long nanos) {
+ /* Same idea as TransferStack.awaitFulfill */
+ long lastTime = (timed)? System.nanoTime() : 0;
+ Thread w = Thread.currentThread();
+ int spins = ((head.next == s) ?
+ (timed? maxTimedSpins : maxUntimedSpins) : 0);
+ for (;;) {
+ if (w.isInterrupted())
+ s.tryCancel(e);
+ Object x = s.item;
+ if (x != e)
+ return x;
+ if (timed) {
+ long now = System.nanoTime();
+ nanos -= now - lastTime;
+ lastTime = now;
+ if (nanos <= 0) {
+ s.tryCancel(e);
+ continue;
+ }
+ }
+ if (spins > 0)
+ --spins;
+ else if (s.waiter == null)
+ s.waiter = w;
+ else if (!timed)
+ LockSupport.park();
+ else if (nanos > spinForTimeoutThreshold)
+ LockSupport.parkNanos(nanos);
}
- return true;
}
/**
- * Waits for a producer to put item placed by consumer, or time out.
+ * Gets rid of cancelled node s with original predecessor pred.
*/
- Object waitForPut(long nanos) throws InterruptedException {
- try {
- if (!tryAcquireNanos(0, nanos) &&
- release(CANCEL))
- return null;
- } catch (InterruptedException ie) {
- checkCancellationOnInterrupt(ie);
+ void clean(QNode pred, QNode s) {
+ s.waiter = null; // forget thread
+ /*
+ * At any given time, exactly one node on list cannot be
+ * deleted -- the last inserted node. To accommodate this,
+ * if we cannot delete s, we save its predecessor as
+ * "cleanMe", deleting the previously saved version
+ * first. At least one of node s or the node previously
+ * saved can always be deleted, so this always terminates.
+ */
+ while (pred.next == s) { // Return early if already unlinked
+ QNode h = head;
+ QNode hn = h.next; // Absorb cancelled first node as head
+ if (hn != null && hn.isCancelled()) {
+ advanceHead(h, hn);
+ continue;
+ }
+ QNode t = tail; // Ensure consistent read for tail
+ if (t == h)
+ return;
+ QNode tn = t.next;
+ if (t != tail)
+ continue;
+ if (tn != null) {
+ advanceTail(t, tn);
+ continue;
+ }
+ if (s != t) { // If not tail, try to unsplice
+ QNode sn = s.next;
+ if (sn == s || pred.casNext(s, sn))
+ return;
+ }
+ QNode dp = cleanMe;
+ if (dp != null) { // Try unlinking previous cancelled node
+ QNode d = dp.next;
+ QNode dn;
+ if (d == null || // d is gone or
+ d == dp || // d is off list or
+ !d.isCancelled() || // d not cancelled or
+ (d != t && // d not tail and
+ (dn = d.next) != null && // has successor
+ dn != d && // that is on list
+ dp.casNext(d, dn))) // d unspliced
+ casCleanMe(dp, null);
+ if (dp == pred)
+ return; // s is already saved node
+ } else if (casCleanMe(null, pred))
+ return; // Postpone cleaning s
}
- return extract();
}
}
/**
+ * The transferer. Set only in constructor, but cannot be declared
+ * as final without further complicating serialization. Since
+ * this is accessed only at most once per public method, there
+ * isn't a noticeable performance penalty for using volatile
+ * instead of final here.
+ */
+ private transient volatile Transferer transferer;
+
+ /**
+ * Creates a <tt>SynchronousQueue</tt> with nonfair access policy.
+ */
+ public SynchronousQueue() {
+ this(false);
+ }
+
+ /**
+ * Creates a <tt>SynchronousQueue</tt> with the specified fairness policy.
+ *
+ * @param fair if true, waiting threads contend in FIFO order for
+ * access; otherwise the order is unspecified.
+ */
+ public SynchronousQueue(boolean fair) {
+ transferer = (fair)? new TransferQueue() : new TransferStack();
+ }
+
+ /**
* Adds the specified element to this queue, waiting if necessary for
* another thread to receive it.
- * @param o the element to add
- * @throws InterruptedException if interrupted while waiting.
- * @throws NullPointerException if the specified element is <tt>null</tt>.
+ *
+ * @throws InterruptedException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
*/
public void put(E o) throws InterruptedException {
if (o == null) throw new NullPointerException();
- final ReentrantLock qlock = this.qlock;
-
- for (;;) {
- Node node;
- boolean mustWait;
- if (Thread.interrupted()) throw new InterruptedException();
- qlock.lock();
- try {
- node = waitingConsumers.deq();
- if ( (mustWait = (node == null)) )
- node = waitingProducers.enq(o);
- } finally {
- qlock.unlock();
- }
-
- if (mustWait) {
- node.waitForTake();
- return;
- }
-
- else if (node.setItem(o))
- return;
-
- // else consumer cancelled, so retry
+ if (transferer.transfer(o, false, 0) == null) {
+ Thread.interrupted();
+ throw new InterruptedException();
}
}
/**
* Inserts the specified element into this queue, waiting if necessary
* up to the specified wait time for another thread to receive it.
- * @param o the element to add
- * @param timeout how long to wait before giving up, in units of
- * <tt>unit</tt>
- * @param unit a <tt>TimeUnit</tt> determining how to interpret the
- * <tt>timeout</tt> parameter
- * @return <tt>true</tt> if successful, or <tt>false</tt> if
- * the specified waiting time elapses before a consumer appears.
- * @throws InterruptedException if interrupted while waiting.
- * @throws NullPointerException if the specified element is <tt>null</tt>.
+ *
+ * @return <tt>true</tt> if successful, or <tt>false</tt> if the
+ * specified waiting time elapses before a consumer appears.
+ * @throws InterruptedException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
*/
- public boolean offer(E o, long timeout, TimeUnit unit) throws InterruptedException {
+ public boolean offer(E o, long timeout, TimeUnit unit)
+ throws InterruptedException {
if (o == null) throw new NullPointerException();
- long nanos = unit.toNanos(timeout);
- final ReentrantLock qlock = this.qlock;
- for (;;) {
- Node node;
- boolean mustWait;
- if (Thread.interrupted()) throw new InterruptedException();
- qlock.lock();
- try {
- node = waitingConsumers.deq();
- if ( (mustWait = (node == null)) )
- node = waitingProducers.enq(o);
- } finally {
- qlock.unlock();
- }
-
- if (mustWait)
- return node.waitForTake(nanos);
-
- else if (node.setItem(o))
- return true;
+ if (transferer.transfer(o, true, unit.toNanos(timeout)) != null)
+ return true;
+ if (!Thread.interrupted())
+ return false;
+ throw new InterruptedException();
+ }
- // else consumer cancelled, so retry
- }
+ /**
+ * Inserts the specified element into this queue, if another thread is
+ * waiting to receive it.
+ *
+ * @param e the element to add
+ * @return <tt>true</tt> if the element was added to this queue, else
+ * <tt>false</tt>
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean offer(E e) {
+ if (e == null) throw new NullPointerException();
+ return transferer.transfer(e, true, 0) != null;
}
/**
* Retrieves and removes the head of this queue, waiting if necessary
* for another thread to insert it.
- * @throws InterruptedException if interrupted while waiting.
+ *
* @return the head of this queue
+ * @throws InterruptedException {@inheritDoc}
*/
public E take() throws InterruptedException {
- final ReentrantLock qlock = this.qlock;
- for (;;) {
- Node node;
- boolean mustWait;
-
- if (Thread.interrupted()) throw new InterruptedException();
- qlock.lock();
- try {
- node = waitingProducers.deq();
- if ( (mustWait = (node == null)) )
- node = waitingConsumers.enq(null);
- } finally {
- qlock.unlock();
- }
-
- if (mustWait) {
- Object x = node.waitForPut();
- return (E)x;
- }
- else {
- Object x = node.getItem();
- if (x != null)
- return (E)x;
- // else cancelled, so retry
- }
- }
+ Object e = transferer.transfer(null, false, 0);
+ if (e != null)
+ return (E)e;
+ Thread.interrupted();
+ throw new InterruptedException();
}
/**
* Retrieves and removes the head of this queue, waiting
* if necessary up to the specified wait time, for another thread
* to insert it.
- * @param timeout how long to wait before giving up, in units of
- * <tt>unit</tt>
- * @param unit a <tt>TimeUnit</tt> determining how to interpret the
- * <tt>timeout</tt> parameter
+ *
* @return the head of this queue, or <tt>null</tt> if the
- * specified waiting time elapses before an element is present.
- * @throws InterruptedException if interrupted while waiting.
+ * specified waiting time elapses before an element is present.
+ * @throws InterruptedException {@inheritDoc}
*/
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
- long nanos = unit.toNanos(timeout);
- final ReentrantLock qlock = this.qlock;
-
- for (;;) {
- Node node;
- boolean mustWait;
-
- if (Thread.interrupted()) throw new InterruptedException();
- qlock.lock();
- try {
- node = waitingProducers.deq();
- if ( (mustWait = (node == null)) )
- node = waitingConsumers.enq(null);
- } finally {
- qlock.unlock();
- }
-
- if (mustWait) {
- Object x = node.waitForPut(nanos);
- return (E)x;
- }
- else {
- Object x = node.getItem();
- if (x != null)
- return (E)x;
- // else cancelled, so retry
- }
- }
- }
-
- // Untimed nonblocking versions
-
- /**
- * Inserts the specified element into this queue, if another thread is
- * waiting to receive it.
- *
- * @param o the element to add.
- * @return <tt>true</tt> if it was possible to add the element to
- * this queue, else <tt>false</tt>
- * @throws NullPointerException if the specified element is <tt>null</tt>
- */
- public boolean offer(E o) {
- if (o == null) throw new NullPointerException();
- final ReentrantLock qlock = this.qlock;
-
- for (;;) {
- Node node;
- qlock.lock();
- try {
- node = waitingConsumers.deq();
- } finally {
- qlock.unlock();
- }
- if (node == null)
- return false;
-
- else if (node.setItem(o))
- return true;
- // else retry
- }
+ Object e = transferer.transfer(null, true, unit.toNanos(timeout));
+ if (e != null || !Thread.interrupted())
+ return (E)e;
+ throw new InterruptedException();
}
/**
@@ -490,30 +885,13 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
* element is available.
*/
public E poll() {
- final ReentrantLock qlock = this.qlock;
- for (;;) {
- Node node;
- qlock.lock();
- try {
- node = waitingProducers.deq();
- } finally {
- qlock.unlock();
- }
- if (node == null)
- return null;
-
- else {
- Object x = node.getItem();
- if (x != null)
- return (E)x;
- // else retry
- }
- }
+ return (E)transferer.transfer(null, true, 0);
}
/**
- * Always returns <tt>true</tt>.
+ * Always returns <tt>true</tt>.
* A <tt>SynchronousQueue</tt> has no internal capacity.
+ *
* @return <tt>true</tt>
*/
public boolean isEmpty() {
@@ -523,6 +901,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
/**
* Always returns zero.
* A <tt>SynchronousQueue</tt> has no internal capacity.
+ *
* @return zero.
*/
public int size() {
@@ -532,6 +911,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
/**
* Always returns zero.
* A <tt>SynchronousQueue</tt> has no internal capacity.
+ *
* @return zero.
*/
public int remainingCapacity() {
@@ -542,11 +922,13 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
* Does nothing.
* A <tt>SynchronousQueue</tt> has no internal capacity.
*/
- public void clear() {}
+ public void clear() {
+ }
/**
* Always returns <tt>false</tt>.
* A <tt>SynchronousQueue</tt> has no internal capacity.
+ *
* @param o the element
* @return <tt>false</tt>
*/
@@ -566,8 +948,9 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
}
/**
- * Returns <tt>false</tt> unless given collection is empty.
+ * Returns <tt>false</tt> unless the given collection is empty.
* A <tt>SynchronousQueue</tt> has no internal capacity.
+ *
* @param c the collection
* @return <tt>false</tt> unless given collection is empty
*/
@@ -578,6 +961,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
/**
* Always returns <tt>false</tt>.
* A <tt>SynchronousQueue</tt> has no internal capacity.
+ *
* @param c the collection
* @return <tt>false</tt>
*/
@@ -588,6 +972,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
/**
* Always returns <tt>false</tt>.
* A <tt>SynchronousQueue</tt> has no internal capacity.
+ *
* @param c the collection
* @return <tt>false</tt>
*/
@@ -596,9 +981,10 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
}
/**
- * Always returns <tt>null</tt>.
+ * Always returns <tt>null</tt>.
* A <tt>SynchronousQueue</tt> does not return elements
* unless actively waited on.
+ *
* @return <tt>null</tt>
*/
public E peek() {
@@ -628,7 +1014,6 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
return new EmptyIterator<E>();
}
-
/**
* Returns a zero-length array.
* @return a zero-length array
@@ -640,8 +1025,10 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
/**
* Sets the zeroeth element of the specified array to <tt>null</tt>
* (if the array has non-zero length) and returns it.
+ *
* @param a the array
* @return the specified array
+ * @throws NullPointerException if the specified array is null
*/
public <T> T[] toArray(T[] a) {
if (a.length > 0)
@@ -649,7 +1036,12 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
return a;
}
-
+ /**
+ * @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();
@@ -664,6 +1056,12 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
return n;
}
+ /**
+ * @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();
@@ -677,9 +1075,54 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
}
return n;
}
-}
+ /*
+ * To cope with serialization strategy in the 1.5 version of
+ * SynchronousQueue, we declare some unused classes and fields
+ * that exist solely to enable serializability across versions.
+ * These fields are never used, so are initialized only if this
+ * object is ever serialized or deserialized.
+ */
+ static class WaitQueue implements java.io.Serializable { }
+ static class LifoWaitQueue extends WaitQueue {
+ private static final long serialVersionUID = -3633113410248163686L;
+ }
+ static class FifoWaitQueue extends WaitQueue {
+ private static final long serialVersionUID = -3623113410248163686L;
+ }
+ private ReentrantLock qlock;
+ private WaitQueue waitingProducers;
+ private WaitQueue waitingConsumers;
+ /**
+ * Save the state to a stream (that is, serialize it).
+ *
+ * @param s the stream
+ */
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+ boolean fair = transferer instanceof TransferQueue;
+ if (fair) {
+ qlock = new ReentrantLock(true);
+ waitingProducers = new FifoWaitQueue();
+ waitingConsumers = new FifoWaitQueue();
+ }
+ else {
+ qlock = new ReentrantLock();
+ waitingProducers = new LifoWaitQueue();
+ waitingConsumers = new LifoWaitQueue();
+ }
+ s.defaultWriteObject();
+ }
+ private void readObject(final java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+ s.defaultReadObject();
+ if (waitingProducers instanceof FifoWaitQueue)
+ transferer = new TransferQueue();
+ else
+ transferer = new TransferStack();
+ }
+}
diff --git a/concurrent/src/main/java/java/util/concurrent/ThreadFactory.java b/concurrent/src/main/java/java/util/concurrent/ThreadFactory.java
index 04c63e1..2f0fb1a 100644
--- a/concurrent/src/main/java/java/util/concurrent/ThreadFactory.java
+++ b/concurrent/src/main/java/java/util/concurrent/ThreadFactory.java
@@ -11,7 +11,7 @@ package java.util.concurrent;
* removes hardwiring of calls to {@link Thread#Thread(Runnable) new Thread},
* enabling applications to use special thread subclasses, priorities, etc.
*
- * <p>
+ * <p>
* The simplest implementation of this interface is just:
* <pre>
* class SimpleThreadFactory implements ThreadFactory {
@@ -23,18 +23,19 @@ package java.util.concurrent;
*
* The {@link Executors#defaultThreadFactory} method provides a more
* useful simple implementation, that sets the created thread context
- * to known values before returning it.
+ * to known values before returning it.
* @since 1.5
* @author Doug Lea
*/
-public interface ThreadFactory {
+public interface ThreadFactory {
/**
- * Constructs a new <tt>Thread</tt>. Implementations may also initialize
- * priority, name, daemon status, <tt>ThreadGroup</tt>, etc.
+ * Constructs a new {@code Thread}. Implementations may also initialize
+ * priority, name, daemon status, {@code ThreadGroup}, etc.
*
* @param r a runnable to be executed by new thread instance
- * @return constructed thread
+ * @return constructed thread, or {@code null} if the request to
+ * create a thread is rejected
*/
Thread newThread(Runnable r);
}
diff --git a/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java b/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
index 1aa8e55..d0c934d 100644
--- a/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
+++ b/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
@@ -6,13 +6,9 @@
package java.util.concurrent;
import java.util.concurrent.locks.*;
+import java.util.concurrent.atomic.*;
import java.util.*;
-// BEGIN android-note
-// Altered {@link TimeUnit#NANOSECONDS} because our javadoc tool doesn't
-// like it.
-// END android-note
-
/**
* An {@link ExecutorService} that executes each submitted task using
* one of possibly several pooled threads, normally configured
@@ -23,7 +19,7 @@ import java.util.*;
* asynchronous tasks, due to reduced per-task invocation overhead,
* and they provide a means of bounding and managing the resources,
* including threads, consumed when executing a collection of tasks.
- * Each <tt>ThreadPoolExecutor</tt> also maintains some basic
+ * Each {@code ThreadPoolExecutor} also maintains some basic
* statistics, such as the number of completed tasks.
*
* <p>To be useful across a wide range of contexts, this class
@@ -42,61 +38,63 @@ import java.util.*;
*
* <dt>Core and maximum pool sizes</dt>
*
- * <dd>A <tt>ThreadPoolExecutor</tt> will automatically adjust the
- * pool size
- * (see {@link ThreadPoolExecutor#getPoolSize})
- * according to the bounds set by corePoolSize
- * (see {@link ThreadPoolExecutor#getCorePoolSize})
- * and
- * maximumPoolSize
- * (see {@link ThreadPoolExecutor#getMaximumPoolSize}).
- * When a new task is submitted in method {@link
- * ThreadPoolExecutor#execute}, and fewer than corePoolSize threads
- * are running, a new thread is created to handle the request, even if
- * other worker threads are idle. If there are more than
- * corePoolSize but less than maximumPoolSize threads running, a new
- * thread will be created only if the queue is full. By setting
- * corePoolSize and maximumPoolSize the same, you create a fixed-size
- * thread pool. By setting maximumPoolSize to an essentially unbounded
- * value such as <tt>Integer.MAX_VALUE</tt>, you allow the pool to
- * accommodate an arbitrary number of concurrent tasks. Most typically,
- * core and maximum pool sizes are set only upon construction, but they
- * may also be changed dynamically using {@link
- * ThreadPoolExecutor#setCorePoolSize} and {@link
- * ThreadPoolExecutor#setMaximumPoolSize}. <dd>
+ * <dd>A {@code ThreadPoolExecutor} will automatically adjust the
+ * pool size (see {@link #getPoolSize})
+ * according to the bounds set by
+ * corePoolSize (see {@link #getCorePoolSize}) and
+ * maximumPoolSize (see {@link #getMaximumPoolSize}).
+ *
+ * When a new task is submitted in method {@link #execute}, and fewer
+ * than corePoolSize threads are running, a new thread is created to
+ * handle the request, even if other worker threads are idle. If
+ * there are more than corePoolSize but less than maximumPoolSize
+ * threads running, a new thread will be created only if the queue is
+ * full. By setting corePoolSize and maximumPoolSize the same, you
+ * create a fixed-size thread pool. By setting maximumPoolSize to an
+ * essentially unbounded value such as {@code Integer.MAX_VALUE}, you
+ * allow the pool to accommodate an arbitrary number of concurrent
+ * tasks. Most typically, core and maximum pool sizes are set only
+ * upon construction, but they may also be changed dynamically using
+ * {@link #setCorePoolSize} and {@link #setMaximumPoolSize}. </dd>
*
- * <dt> On-demand construction
+ * <dt>On-demand construction</dt>
*
* <dd> By default, even core threads are initially created and
- * started only when needed by new tasks, but this can be overridden
- * dynamically using method {@link
- * ThreadPoolExecutor#prestartCoreThread} or
- * {@link ThreadPoolExecutor#prestartAllCoreThreads}. </dd>
+ * started only when new tasks arrive, but this can be overridden
+ * dynamically using method {@link #prestartCoreThread} or {@link
+ * #prestartAllCoreThreads}. You probably want to prestart threads if
+ * you construct the pool with a non-empty queue. </dd>
*
* <dt>Creating new threads</dt>
*
- * <dd>New threads are created using a {@link
- * java.util.concurrent.ThreadFactory}. If not otherwise specified, a
- * {@link Executors#defaultThreadFactory} is used, that creates threads to all
- * be in the same {@link ThreadGroup} and with the same
- * <tt>NORM_PRIORITY</tt> priority and non-daemon status. By supplying
- * a different ThreadFactory, you can alter the thread's name, thread
- * group, priority, daemon status, etc. </dd>
+ * <dd>New threads are created using a {@link ThreadFactory}. If not
+ * otherwise specified, a {@link Executors#defaultThreadFactory} is
+ * used, that creates threads to all be in the same {@link
+ * ThreadGroup} and with the same {@code NORM_PRIORITY} priority and
+ * non-daemon status. By supplying a different ThreadFactory, you can
+ * alter the thread's name, thread group, priority, daemon status,
+ * etc. If a {@code ThreadFactory} fails to create a thread when asked
+ * by returning null from {@code newThread}, the executor will
+ * continue, but might not be able to execute any tasks. Threads
+ * should possess the "modifyThread" {@code RuntimePermission}. If
+ * worker threads or other threads using the pool do not possess this
+ * permission, service may be degraded: configuration changes may not
+ * take effect in a timely manner, and a shutdown pool may remain in a
+ * state in which termination is possible but not completed.</dd>
*
* <dt>Keep-alive times</dt>
*
* <dd>If the pool currently has more than corePoolSize threads,
* excess threads will be terminated if they have been idle for more
- * than the keepAliveTime (see {@link
- * ThreadPoolExecutor#getKeepAliveTime}). This provides a means of
- * reducing resource consumption when the pool is not being actively
- * used. If the pool becomes more active later, new threads will be
- * constructed. This parameter can also be changed dynamically
- * using method {@link ThreadPoolExecutor#setKeepAliveTime}. Using
- * a value of <tt>Long.MAX_VALUE</tt> <code>TimeUnit.NANOSECONDS</code>
- * effectively disables idle threads from ever terminating prior
- * to shut down.
- * </dd>
+ * than the keepAliveTime (see {@link #getKeepAliveTime}). This
+ * provides a means of reducing resource consumption when the pool is
+ * not being actively used. If the pool becomes more active later, new
+ * threads will be constructed. This parameter can also be changed
+ * dynamically using method {@link #setKeepAliveTime}. Using a value
+ * of {@code Long.MAX_VALUE} {@link TimeUnit#NANOSECONDS} effectively
+ * disables idle threads from ever terminating prior to shut down. The
+ * keep-alive policy applies only when there are more than
+ * corePoolSizeThreads.</dd>
*
* <dt>Queuing</dt>
*
@@ -112,7 +110,7 @@ import java.util.*;
* <li> If corePoolSize or more threads are running, the Executor
* always prefers queuing a request rather than adding a new
* thread.</li>
- *
+ *
* <li> If a request cannot be queued, a new thread is created unless
* this would exceed maximumPoolSize, in which case, the task will be
* rejected.</li>
@@ -135,7 +133,7 @@ import java.util.*;
*
* <li><em> Unbounded queues.</em> Using an unbounded queue (for
* example a {@link LinkedBlockingQueue} without a predefined
- * capacity) will cause new tasks to be queued in cases where all
+ * capacity) will cause new tasks to wait in the queue when all
* corePoolSize threads are busy. Thus, no more than corePoolSize
* threads will ever be created. (And the value of the maximumPoolSize
* therefore doesn't have any effect.) This may be appropriate when
@@ -165,35 +163,33 @@ import java.util.*;
*
* <dt>Rejected tasks</dt>
*
- * <dd> New tasks submitted in method {@link
- * ThreadPoolExecutor#execute} will be <em>rejected</em> when the
- * Executor has been shut down, and also when the Executor uses finite
- * bounds for both maximum threads and work queue capacity, and is
- * saturated. In either case, the <tt>execute</tt> method invokes the
- * {@link RejectedExecutionHandler#rejectedExecution} method of its
- * {@link RejectedExecutionHandler}. Four predefined handler policies
- * are provided:
+ * <dd> New tasks submitted in method {@link #execute} will be
+ * <em>rejected</em> when the Executor has been shut down, and also
+ * when the Executor uses finite bounds for both maximum threads and
+ * work queue capacity, and is saturated. In either case, the {@code
+ * execute} method invokes the {@link
+ * RejectedExecutionHandler#rejectedExecution} method of its {@link
+ * RejectedExecutionHandler}. Four predefined handler policies are
+ * provided:
*
* <ol>
*
- * <li> In the
- * default {@link ThreadPoolExecutor.AbortPolicy}, the handler throws a
- * runtime {@link RejectedExecutionException} upon rejection. </li>
- *
- * <li> In {@link
- * ThreadPoolExecutor.CallerRunsPolicy}, the thread that invokes
- * <tt>execute</tt> itself runs the task. This provides a simple
- * feedback control mechanism that will slow down the rate that new
- * tasks are submitted. </li>
+ * <li> In the default {@link ThreadPoolExecutor.AbortPolicy}, the
+ * handler throws a runtime {@link RejectedExecutionException} upon
+ * rejection. </li>
*
- * <li> In {@link ThreadPoolExecutor.DiscardPolicy},
- * a task that cannot be executed is simply dropped. </li>
+ * <li> In {@link ThreadPoolExecutor.CallerRunsPolicy}, the thread
+ * that invokes {@code execute} itself runs the task. This provides a
+ * simple feedback control mechanism that will slow down the rate that
+ * new tasks are submitted. </li>
*
- * <li>In {@link
- * ThreadPoolExecutor.DiscardOldestPolicy}, if the executor is not
- * shut down, the task at the head of the work queue is dropped, and
- * then execution is retried (which can fail again, causing this to be
- * repeated.) </li>
+ * <li> In {@link ThreadPoolExecutor.DiscardPolicy}, a task that
+ * cannot be executed is simply dropped. </li>
+ *
+ * <li>In {@link ThreadPoolExecutor.DiscardOldestPolicy}, if the
+ * executor is not shut down, the task at the head of the work queue
+ * is dropped, and then execution is retried (which can fail again,
+ * causing this to be repeated.) </li>
*
* </ol>
*
@@ -204,50 +200,62 @@ import java.util.*;
*
* <dt>Hook methods</dt>
*
- * <dd>This class provides <tt>protected</tt> overridable {@link
- * ThreadPoolExecutor#beforeExecute} and {@link
- * ThreadPoolExecutor#afterExecute} methods that are called before and
- * after execution of each task. These can be used to manipulate the
- * execution environment, for example, reinitializing ThreadLocals,
- * gathering statistics, or adding log entries. Additionally, method
- * {@link ThreadPoolExecutor#terminated} can be overridden to perform
- * any special processing that needs to be done once the Executor has
- * fully terminated.</dd>
+ * <dd>This class provides {@code protected} overridable {@link
+ * #beforeExecute} and {@link #afterExecute} methods that are called
+ * before and after execution of each task. These can be used to
+ * manipulate the execution environment; for example, reinitializing
+ * ThreadLocals, gathering statistics, or adding log
+ * entries. Additionally, method {@link #terminated} can be overridden
+ * to perform any special processing that needs to be done once the
+ * Executor has fully terminated.
+ *
+ * <p>If hook or callback methods throw exceptions, internal worker
+ * threads may in turn fail and abruptly terminate.</dd>
*
* <dt>Queue maintenance</dt>
*
- * <dd> Method {@link ThreadPoolExecutor#getQueue} allows access to
- * the work queue for purposes of monitoring and debugging. Use of
- * this method for any other purpose is strongly discouraged. Two
- * supplied methods, {@link ThreadPoolExecutor#remove} and {@link
- * ThreadPoolExecutor#purge} are available to assist in storage
- * reclamation when large numbers of queued tasks become
- * cancelled.</dd> </dl>
+ * <dd> Method {@link #getQueue} allows access to the work queue for
+ * purposes of monitoring and debugging. Use of this method for any
+ * other purpose is strongly discouraged. Two supplied methods,
+ * {@link #remove} and {@link #purge} are available to assist in
+ * storage reclamation when large numbers of queued tasks become
+ * cancelled.</dd>
+ *
+ * <dt>Finalization</dt>
+ *
+ * <dd> A pool that is no longer referenced in a program <em>AND</em>
+ * has no remaining threads will be {@code shutdown} automatically. If
+ * you would like to ensure that unreferenced pools are reclaimed even
+ * if users forget to call {@link #shutdown}, then you must arrange
+ * that unused threads eventually die, by setting appropriate
+ * keep-alive times using a lower bound of zero core threads. </dd>
+ *
+ * </dl>
*
* <p> <b>Extension example</b>. Most extensions of this class
* override one or more of the protected hook methods. For example,
* here is a subclass that adds a simple pause/resume feature:
*
- * <pre>
+ * <pre> {@code
* class PausableThreadPoolExecutor extends ThreadPoolExecutor {
* private boolean isPaused;
* private ReentrantLock pauseLock = new ReentrantLock();
* private Condition unpaused = pauseLock.newCondition();
*
* public PausableThreadPoolExecutor(...) { super(...); }
- *
+ *
* protected void beforeExecute(Thread t, Runnable r) {
* super.beforeExecute(t, r);
* pauseLock.lock();
* try {
* while (isPaused) unpaused.await();
- * } catch(InterruptedException ie) {
+ * } catch (InterruptedException ie) {
* t.interrupt();
* } finally {
* pauseLock.unlock();
* }
* }
- *
+ *
* public void pause() {
* pauseLock.lock();
* try {
@@ -256,7 +264,7 @@ import java.util.*;
* pauseLock.unlock();
* }
* }
- *
+ *
* public void resume() {
* pauseLock.lock();
* try {
@@ -266,86 +274,196 @@ import java.util.*;
* pauseLock.unlock();
* }
* }
- * }
- * </pre>
+ * }}</pre>
+ *
* @since 1.5
* @author Doug Lea
*/
public class ThreadPoolExecutor extends AbstractExecutorService {
/**
- * Only used to force toArray() to produce a Runnable[].
+ * The main pool control state, ctl, is an atomic integer packing
+ * two conceptual fields
+ * workerCount, indicating the effective number of threads
+ * runState, indicating whether running, shutting down etc
+ *
+ * In order to pack them into one int, we limit workerCount to
+ * (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2
+ * billion) otherwise representable. If this is ever an issue in
+ * the future, the variable can be changed to be an AtomicLong,
+ * and the shift/mask constants below adjusted. But until the need
+ * arises, this code is a bit faster and simpler using an int.
+ *
+ * The workerCount is the number of workers that have been
+ * permitted to start and not permitted to stop. The value may be
+ * transiently different from the actual number of live threads,
+ * for example when a ThreadFactory fails to create a thread when
+ * asked, and when exiting threads are still performing
+ * bookkeeping before terminating. The user-visible pool size is
+ * reported as the current size of the workers set.
+ *
+ * The runState provides the main lifecyle control, taking on values:
+ *
+ * RUNNING: Accept new tasks and process queued tasks
+ * SHUTDOWN: Don't accept new tasks, but process queued tasks
+ * STOP: Don't accept new tasks, don't process queued tasks,
+ * and interrupt in-progress tasks
+ * TIDYING: All tasks have terminated, workerCount is zero,
+ * the thread transitioning to state TIDYING
+ * will run the terminated() hook method
+ * TERMINATED: terminated() has completed
+ *
+ * The numerical order among these values matters, to allow
+ * ordered comparisons. The runState monotonically increases over
+ * time, but need not hit each state. The transitions are:
+ *
+ * RUNNING -> SHUTDOWN
+ * On invocation of shutdown(), perhaps implicitly in finalize()
+ * (RUNNING or SHUTDOWN) -> STOP
+ * On invocation of shutdownNow()
+ * SHUTDOWN -> TIDYING
+ * When both queue and pool are empty
+ * STOP -> TIDYING
+ * When pool is empty
+ * TIDYING -> TERMINATED
+ * When the terminated() hook method has completed
+ *
+ * Threads waiting in awaitTermination() will return when the
+ * state reaches TERMINATED.
+ *
+ * Detecting the transition from SHUTDOWN to TIDYING is less
+ * straightforward than you'd like because the queue may become
+ * empty after non-empty and vice versa during SHUTDOWN state, but
+ * we can only terminate if, after seeing that it is empty, we see
+ * that workerCount is 0 (which sometimes entails a recheck -- see
+ * below).
*/
- private static final Runnable[] EMPTY_RUNNABLE_ARRAY = new Runnable[0];
+ private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
+ private static final int COUNT_BITS = Integer.SIZE - 3;
+ private static final int CAPACITY = (1 << COUNT_BITS) - 1;
+
+ // runState is stored in the high-order bits
+ private static final int RUNNING = -1 << COUNT_BITS;
+ private static final int SHUTDOWN = 0 << COUNT_BITS;
+ private static final int STOP = 1 << COUNT_BITS;
+ private static final int TIDYING = 2 << COUNT_BITS;
+ private static final int TERMINATED = 3 << COUNT_BITS;
+
+ // Packing and unpacking ctl
+ private static int runStateOf(int c) { return c & ~CAPACITY; }
+ private static int workerCountOf(int c) { return c & CAPACITY; }
+ private static int ctlOf(int rs, int wc) { return rs | wc; }
+
+ /*
+ * Bit field accessors that don't require unpacking ctl.
+ * These depend on the bit layout and on workerCount being never negative.
+ */
+
+ private static boolean runStateLessThan(int c, int s) {
+ return c < s;
+ }
+
+ private static boolean runStateAtLeast(int c, int s) {
+ return c >= s;
+ }
+
+ private static boolean isRunning(int c) {
+ return c < SHUTDOWN;
+ }
/**
- * Permission for checking shutdown
+ * Attempt to CAS-increment the workerCount field of ctl.
*/
- private static final RuntimePermission shutdownPerm =
- new RuntimePermission("modifyThread");
+ private boolean compareAndIncrementWorkerCount(int expect) {
+ return ctl.compareAndSet(expect, expect + 1);
+ }
/**
- * Queue used for holding tasks and handing off to worker threads.
+ * Attempt to CAS-decrement the workerCount field of ctl.
*/
- private final BlockingQueue<Runnable> workQueue;
+ private boolean compareAndDecrementWorkerCount(int expect) {
+ return ctl.compareAndSet(expect, expect - 1);
+ }
/**
- * Lock held on updates to poolSize, corePoolSize, maximumPoolSize, and
- * workers set.
+ * Decrements the workerCount field of ctl. This is called only on
+ * abrupt termination of a thread (see processWorkerExit). Other
+ * decrements are performed within getTask.
*/
- private final ReentrantLock mainLock = new ReentrantLock();
+ private void decrementWorkerCount() {
+ do {} while (! compareAndDecrementWorkerCount(ctl.get()));
+ }
/**
- * Wait condition to support awaitTermination
+ * The queue used for holding tasks and handing off to worker
+ * threads. We do not require that workQueue.poll() returning
+ * null necessarily means that workQueue.isEmpty(), so rely
+ * solely on isEmpty to see if the queue is empty (which we must
+ * do for example when deciding whether to transition from
+ * SHUTDOWN to TIDYING). This accommodates special-purpose
+ * queues such as DelayQueues for which poll() is allowed to
+ * return null even if it may later return non-null when delays
+ * expire.
*/
- private final Condition termination = mainLock.newCondition();
+ private final BlockingQueue<Runnable> workQueue;
/**
- * Set containing all worker threads in pool.
+ * Lock held on access to workers set and related bookkeeping.
+ * While we could use a concurrent set of some sort, it turns out
+ * to be generally preferable to use a lock. Among the reasons is
+ * that this serializes interruptIdleWorkers, which avoids
+ * unnecessary interrupt storms, especially during shutdown.
+ * Otherwise exiting threads would concurrently interrupt those
+ * that have not yet interrupted. It also simplifies some of the
+ * associated statistics bookkeeping of largestPoolSize etc. We
+ * also hold mainLock on shutdown and shutdownNow, for the sake of
+ * ensuring workers set is stable while separately checking
+ * permission to interrupt and actually interrupting.
*/
- private final HashSet<Worker> workers = new HashSet<Worker>();
+ private final ReentrantLock mainLock = new ReentrantLock();
/**
- * Timeout in nanoseconds for idle threads waiting for work.
- * Threads use this timeout only when there are more than
- * corePoolSize present. Otherwise they wait forever for new work.
+ * Set containing all worker threads in pool. Accessed only when
+ * holding mainLock.
*/
- private volatile long keepAliveTime;
+ private final HashSet<Worker> workers = new HashSet<Worker>();
/**
- * Core pool size, updated only while holding mainLock,
- * but volatile to allow concurrent readability even
- * during updates.
+ * Wait condition to support awaitTermination
*/
- private volatile int corePoolSize;
+ private final Condition termination = mainLock.newCondition();
/**
- * Maximum pool size, updated only while holding mainLock
- * but volatile to allow concurrent readability even
- * during updates.
+ * Tracks largest attained pool size. Accessed only under
+ * mainLock.
*/
- private volatile int maximumPoolSize;
+ private int largestPoolSize;
/**
- * Current pool size, updated only while holding mainLock
- * but volatile to allow concurrent readability even
- * during updates.
+ * Counter for completed tasks. Updated only on termination of
+ * worker threads. Accessed only under mainLock.
*/
- private volatile int poolSize;
+ private long completedTaskCount;
- /**
- * Lifecycle state
+ /*
+ * All user control parameters are declared as volatiles so that
+ * ongoing actions are based on freshest values, but without need
+ * for locking, since no internal invariants depend on them
+ * changing synchronously with respect to other actions.
*/
- volatile int runState;
- // Special values for runState
- /** Normal, not-shutdown mode */
- static final int RUNNING = 0;
- /** Controlled shutdown mode */
- static final int SHUTDOWN = 1;
- /** Immediate shutdown mode */
- static final int STOP = 2;
- /** Final state */
- static final int TERMINATED = 3;
+ /**
+ * Factory for new threads. All threads are created using this
+ * factory (via method addWorker). All callers must be prepared
+ * for addWorker to fail, which may reflect a system or user's
+ * policy limiting the number of threads. Even though it is not
+ * treated as an error, failure to create threads may result in
+ * new tasks being rejected or existing ones remaining stuck in
+ * the queue. On the other hand, no special precautions exist to
+ * handle OutOfMemoryErrors that might be thrown while trying to
+ * create threads, since there is generally no recourse from
+ * within this class.
+ */
+ private volatile ThreadFactory threadFactory;
/**
* Handler called when saturated or shutdown in execute.
@@ -353,21 +471,24 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
private volatile RejectedExecutionHandler handler;
/**
- * Factory for new threads.
+ * Timeout in nanoseconds for idle threads waiting for work.
+ * Threads use this timeout when there are more than corePoolSize
+ * present. Otherwise they wait forever for new work.
*/
- private volatile ThreadFactory threadFactory;
+ private volatile long keepAliveTime;
/**
- * Tracks largest attained pool size.
+ * Core pool size is the minimum number of workers to keep alive
+ * (and not allow to time out etc).
*/
- private int largestPoolSize;
+ private volatile int corePoolSize;
/**
- * Counter for completed tasks. Updated only on termination of
- * worker threads.
+ * Maximum pool size. Note that the actual maximum is internally
+ * bounded by CAPACITY.
*/
- private long completedTaskCount;
-
+ private volatile int maximumPoolSize;
+
/**
* The default rejected execution handler
*/
@@ -375,336 +496,622 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
new AbortPolicy();
/**
- * Invoke the rejected execution handler for the given command.
+ * Permission required for callers of shutdown and shutdownNow.
+ * We additionally require (see checkShutdownAccess) that callers
+ * have permission to actually interrupt threads in the worker set
+ * (as governed by Thread.interrupt, which relies on
+ * ThreadGroup.checkAccess, which in turn relies on
+ * SecurityManager.checkAccess). Shutdowns are attempted only if
+ * these checks pass.
+ *
+ * All actual invocations of Thread.interrupt (see
+ * interruptIdleWorkers and interruptWorkers) ignore
+ * SecurityExceptions, meaning that the attempted interrupts
+ * silently fail. In the case of shutdown, they should not fail
+ * unless the SecurityManager has inconsistent policies, sometimes
+ * allowing access to a thread and sometimes not. In such cases,
+ * failure to actually interrupt threads may disable or delay full
+ * termination. Other uses of interruptIdleWorkers are advisory,
+ * and failure to actually interrupt will merely delay response to
+ * configuration changes so is not handled exceptionally.
*/
- void reject(Runnable command) {
- handler.rejectedExecution(command, this);
+ private static final RuntimePermission shutdownPerm =
+ new RuntimePermission("modifyThread");
+
+ /**
+ * Class Worker mainly maintains interrupt control state for
+ * threads running tasks, along with other minor bookkeeping.
+ * This class opportunistically extends AbstractQueuedSynchronizer
+ * to simplify acquiring and releasing a lock surrounding each
+ * task execution. This protects against interrupts that are
+ * intended to wake up a worker thread waiting for a task from
+ * instead interrupting a task being run. We implement a simple
+ * non-reentrant mutual exclusion lock rather than use ReentrantLock
+ * because we do not want worker tasks to be able to reacquire the
+ * lock when they invoke pool control methods like setCorePoolSize.
+ */
+ private final class Worker
+ extends AbstractQueuedSynchronizer
+ implements Runnable
+ {
+ /**
+ * This class will never be serialized, but we provide a
+ * serialVersionUID to suppress a javac warning.
+ */
+ private static final long serialVersionUID = 6138294804551838833L;
+
+ /** Thread this worker is running in. Null if factory fails. */
+ final Thread thread;
+ /** Initial task to run. Possibly null. */
+ Runnable firstTask;
+ /** Per-thread task counter */
+ volatile long completedTasks;
+
+ /**
+ * Creates with given first task and thread from ThreadFactory.
+ * @param firstTask the first task (null if none)
+ */
+ Worker(Runnable firstTask) {
+ this.firstTask = firstTask;
+ this.thread = getThreadFactory().newThread(this);
+ }
+
+ /** Delegates main run loop to outer runWorker */
+ public void run() {
+ runWorker(this);
+ }
+
+ // Lock methods
+ //
+ // The value 0 represents the unlocked state.
+ // The value 1 represents the locked state.
+
+ protected boolean isHeldExclusively() {
+ return getState() == 1;
+ }
+
+ protected boolean tryAcquire(int unused) {
+ if (compareAndSetState(0, 1)) {
+ setExclusiveOwnerThread(Thread.currentThread());
+ return true;
+ }
+ return false;
+ }
+
+ protected boolean tryRelease(int unused) {
+ setExclusiveOwnerThread(null);
+ setState(0);
+ return true;
+ }
+
+ public void lock() { acquire(1); }
+ public boolean tryLock() { return tryAcquire(1); }
+ public void unlock() { release(1); }
+ public boolean isLocked() { return isHeldExclusively(); }
}
+ /*
+ * Methods for setting control state
+ */
+
/**
- * Create and return a new thread running firstTask as its first
- * task. Call only while holding mainLock
- * @param firstTask the task the new thread should run first (or
- * null if none)
- * @return the new thread
+ * Transitions runState to given target, or leaves it alone if
+ * already at least the given target.
+ *
+ * @param targetState the desired state, either SHUTDOWN or STOP
+ * (but not TIDYING or TERMINATED -- use tryTerminate for that)
*/
- private Thread addThread(Runnable firstTask) {
- Worker w = new Worker(firstTask);
- Thread t = threadFactory.newThread(w);
- w.thread = t;
- workers.add(w);
- int nt = ++poolSize;
- if (nt > largestPoolSize)
- largestPoolSize = nt;
- return t;
+ private void advanceRunState(int targetState) {
+ for (;;) {
+ int c = ctl.get();
+ if (runStateAtLeast(c, targetState) ||
+ ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
+ break;
+ }
}
/**
- * Create and start a new thread running firstTask as its first
- * task, only if fewer than corePoolSize threads are running.
- * @param firstTask the task the new thread should run first (or
- * null if none)
- * @return true if successful.
+ * Transitions to TERMINATED state if either (SHUTDOWN and pool
+ * and queue empty) or (STOP and pool empty). If otherwise
+ * eligible to terminate but workerCount is nonzero, interrupts an
+ * idle worker to ensure that shutdown signals propagate. This
+ * method must be called following any action that might make
+ * termination possible -- reducing worker count or removing tasks
+ * from the queue during shutdown. The method is non-private to
+ * allow access from ScheduledThreadPoolExecutor.
+ */
+ final void tryTerminate() {
+ for (;;) {
+ int c = ctl.get();
+ if (isRunning(c) ||
+ runStateAtLeast(c, TIDYING) ||
+ (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
+ return;
+ if (workerCountOf(c) != 0) { // Eligible to terminate
+ interruptIdleWorkers(ONLY_ONE);
+ return;
+ }
+
+ final ReentrantLock mainLock = this.mainLock;
+ mainLock.lock();
+ try {
+ if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
+ try {
+ terminated();
+ } finally {
+ ctl.set(ctlOf(TERMINATED, 0));
+ termination.signalAll();
+ }
+ return;
+ }
+ } finally {
+ mainLock.unlock();
+ }
+ // else retry on failed CAS
+ }
+ }
+
+ /*
+ * Methods for controlling interrupts to worker threads.
*/
- private boolean addIfUnderCorePoolSize(Runnable firstTask) {
- Thread t = null;
+
+ /**
+ * If there is a security manager, makes sure caller has
+ * permission to shut down threads in general (see shutdownPerm).
+ * If this passes, additionally makes sure the caller is allowed
+ * to interrupt each worker thread. This might not be true even if
+ * first check passed, if the SecurityManager treats some threads
+ * specially.
+ */
+ private void checkShutdownAccess() {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ security.checkPermission(shutdownPerm);
+ final ReentrantLock mainLock = this.mainLock;
+ mainLock.lock();
+ try {
+ for (Worker w : workers)
+ security.checkAccess(w.thread);
+ } finally {
+ mainLock.unlock();
+ }
+ }
+ }
+
+ /**
+ * Interrupts all threads, even if active. Ignores SecurityExceptions
+ * (in which case some threads may remain uninterrupted).
+ */
+ private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
- if (poolSize < corePoolSize)
- t = addThread(firstTask);
+ for (Worker w : workers) {
+ try {
+ w.thread.interrupt();
+ } catch (SecurityException ignore) {
+ }
+ }
} finally {
mainLock.unlock();
}
- if (t == null)
- return false;
- t.start();
- return true;
}
/**
- * Create and start a new thread only if fewer than maximumPoolSize
- * threads are running. The new thread runs as its first task the
- * next task in queue, or if there is none, the given task.
- * @param firstTask the task the new thread should run first (or
- * null if none)
- * @return null on failure, else the first task to be run by new thread.
+ * Interrupts threads that might be waiting for tasks (as
+ * indicated by not being locked) so they can check for
+ * termination or configuration changes. Ignores
+ * SecurityExceptions (in which case some threads may remain
+ * uninterrupted).
+ *
+ * @param onlyOne If true, interrupt at most one worker. This is
+ * called only from tryTerminate when termination is otherwise
+ * enabled but there are still other workers. In this case, at
+ * most one waiting worker is interrupted to propagate shutdown
+ * signals in case all threads are currently waiting.
+ * Interrupting any arbitrary thread ensures that newly arriving
+ * workers since shutdown began will also eventually exit.
+ * To guarantee eventual termination, it suffices to always
+ * interrupt only one idle worker, but shutdown() interrupts all
+ * idle workers so that redundant workers exit promptly, not
+ * waiting for a straggler task to finish.
*/
- private Runnable addIfUnderMaximumPoolSize(Runnable firstTask) {
- Thread t = null;
- Runnable next = null;
+ private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
- if (poolSize < maximumPoolSize) {
- next = workQueue.poll();
- if (next == null)
- next = firstTask;
- t = addThread(next);
+ for (Worker w : workers) {
+ Thread t = w.thread;
+ if (!t.isInterrupted() && w.tryLock()) {
+ try {
+ t.interrupt();
+ } catch (SecurityException ignore) {
+ } finally {
+ w.unlock();
+ }
+ }
+ if (onlyOne)
+ break;
}
} finally {
mainLock.unlock();
}
- if (t == null)
- return null;
- t.start();
- return next;
}
+ /**
+ * Common form of interruptIdleWorkers, to avoid having to
+ * remember what the boolean argument means.
+ */
+ private void interruptIdleWorkers() {
+ interruptIdleWorkers(false);
+ }
+
+ private static final boolean ONLY_ONE = true;
/**
- * Get the next task for a worker thread to run.
- * @return the task
- * @throws InterruptedException if interrupted while waiting for task
+ * Ensures that unless the pool is stopping, the current thread
+ * does not have its interrupt set. This requires a double-check
+ * of state in case the interrupt was cleared concurrently with a
+ * shutdownNow -- if so, the interrupt is re-enabled.
*/
- Runnable getTask() throws InterruptedException {
- for (;;) {
- switch(runState) {
- case RUNNING: {
- if (poolSize <= corePoolSize) // untimed wait if core
- return workQueue.take();
-
- long timeout = keepAliveTime;
- if (timeout <= 0) // die immediately for 0 timeout
- return null;
- Runnable r = workQueue.poll(timeout, TimeUnit.NANOSECONDS);
- if (r != null)
- return r;
- if (poolSize > corePoolSize) // timed out
- return null;
- // else, after timeout, pool shrank so shouldn't die, so retry
- break;
- }
+ private void clearInterruptsForTaskRun() {
+ if (runStateLessThan(ctl.get(), STOP) &&
+ Thread.interrupted() &&
+ runStateAtLeast(ctl.get(), STOP))
+ Thread.currentThread().interrupt();
+ }
- case SHUTDOWN: {
- // Help drain queue
- Runnable r = workQueue.poll();
- if (r != null)
- return r;
-
- // Check if can terminate
- if (workQueue.isEmpty()) {
- interruptIdleWorkers();
- return null;
- }
+ /*
+ * Misc utilities, most of which are also exported to
+ * ScheduledThreadPoolExecutor
+ */
- // There could still be delayed tasks in queue.
- // Wait for one, re-checking state upon interruption
- try {
- return workQueue.take();
- } catch(InterruptedException ignore) {}
- break;
- }
+ /**
+ * Invokes the rejected execution handler for the given command.
+ * Package-protected for use by ScheduledThreadPoolExecutor.
+ */
+ final void reject(Runnable command) {
+ handler.rejectedExecution(command, this);
+ }
- case STOP:
- return null;
- default:
- assert false;
+ /**
+ * Performs any further cleanup following run state transition on
+ * invocation of shutdown. A no-op here, but used by
+ * ScheduledThreadPoolExecutor to cancel delayed tasks.
+ */
+ void onShutdown() {
+ }
+
+ /**
+ * State check needed by ScheduledThreadPoolExecutor to
+ * enable running tasks during shutdown.
+ *
+ * @param shutdownOK true if should return true if SHUTDOWN
+ */
+ final boolean isRunningOrShutdown(boolean shutdownOK) {
+ int rs = runStateOf(ctl.get());
+ return rs == RUNNING || (rs == SHUTDOWN && shutdownOK);
+ }
+
+ /**
+ * Drains the task queue into a new list, normally using
+ * drainTo. But if the queue is a DelayQueue or any other kind of
+ * queue for which poll or drainTo may fail to remove some
+ * elements, it deletes them one by one.
+ */
+ private List<Runnable> drainQueue() {
+ BlockingQueue<Runnable> q = workQueue;
+ List<Runnable> taskList = new ArrayList<Runnable>();
+ q.drainTo(taskList);
+ if (!q.isEmpty()) {
+ for (Runnable r : q.toArray(new Runnable[0])) {
+ if (q.remove(r))
+ taskList.add(r);
}
}
+ return taskList;
}
+ /*
+ * Methods for creating, running and cleaning up after workers
+ */
+
/**
- * Wake up all threads that might be waiting for tasks.
+ * Checks if a new worker can be added with respect to current
+ * pool state and the given bound (either core or maximum). If so,
+ * the worker count is adjusted accordingly, and, if possible, a
+ * new worker is created and started running firstTask as its
+ * first task. This method returns false if the pool is stopped or
+ * eligible to shut down. It also returns false if the thread
+ * factory fails to create a thread when asked, which requires a
+ * backout of workerCount, and a recheck for termination, in case
+ * the existence of this worker was holding up termination.
+ *
+ * @param firstTask the task the new thread should run first (or
+ * null if none). Workers are created with an initial first task
+ * (in method execute()) to bypass queuing when there are fewer
+ * than corePoolSize threads (in which case we always start one),
+ * or when the queue is full (in which case we must bypass queue).
+ * Initially idle threads are usually created via
+ * prestartCoreThread or to replace other dying workers.
+ *
+ * @param core if true use corePoolSize as bound, else
+ * maximumPoolSize. (A boolean indicator is used here rather than a
+ * value to ensure reads of fresh values after checking other pool
+ * state).
+ * @return true if successful
*/
- void interruptIdleWorkers() {
+ private boolean addWorker(Runnable firstTask, boolean core) {
+ retry:
+ for (;;) {
+ int c = ctl.get();
+ int rs = runStateOf(c);
+
+ // Check if queue empty only if necessary.
+ if (rs >= SHUTDOWN &&
+ ! (rs == SHUTDOWN &&
+ firstTask == null &&
+ ! workQueue.isEmpty()))
+ return false;
+
+ for (;;) {
+ int wc = workerCountOf(c);
+ if (wc >= CAPACITY ||
+ wc >= (core ? corePoolSize : maximumPoolSize))
+ return false;
+ if (compareAndIncrementWorkerCount(c))
+ break retry;
+ c = ctl.get(); // Re-read ctl
+ if (runStateOf(c) != rs)
+ continue retry;
+ // else CAS failed due to workerCount change; retry inner loop
+ }
+ }
+
+ Worker w = new Worker(firstTask);
+ Thread t = w.thread;
+
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
- for (Worker w : workers)
- w.interruptIfIdle();
+ // Recheck while holding lock.
+ // Back out on ThreadFactory failure or if
+ // shut down before lock acquired.
+ int c = ctl.get();
+ int rs = runStateOf(c);
+
+ if (t == null ||
+ (rs >= SHUTDOWN &&
+ ! (rs == SHUTDOWN &&
+ firstTask == null))) {
+ decrementWorkerCount();
+ tryTerminate();
+ return false;
+ }
+
+ workers.add(w);
+
+ int s = workers.size();
+ if (s > largestPoolSize)
+ largestPoolSize = s;
} finally {
mainLock.unlock();
}
+
+ t.start();
+ // It is possible (but unlikely) for a thread to have been
+ // added to workers, but not yet started, during transition to
+ // STOP, which could result in a rare missed interrupt,
+ // because Thread.interrupt is not guaranteed to have any effect
+ // on a non-yet-started Thread (see Thread#interrupt).
+ if (runStateOf(ctl.get()) == STOP && ! t.isInterrupted())
+ t.interrupt();
+
+ return true;
}
/**
- * Perform bookkeeping for a terminated worker thread.
+ * Performs cleanup and bookkeeping for a dying worker. Called
+ * only from worker threads. Unless completedAbruptly is set,
+ * assumes that workerCount has already been adjusted to account
+ * for exit. This method removes thread from worker set, and
+ * possibly terminates the pool or replaces the worker if either
+ * it exited due to user task exception or if fewer than
+ * corePoolSize workers are running or queue is non-empty but
+ * there are no workers.
+ *
* @param w the worker
+ * @param completedAbruptly if the worker died due to user exception
*/
- void workerDone(Worker w) {
+ private void processWorkerExit(Worker w, boolean completedAbruptly) {
+ if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
+ decrementWorkerCount();
+
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
- if (--poolSize > 0)
- return;
-
- // Else, this is the last thread. Deal with potential shutdown.
-
- int state = runState;
- assert state != TERMINATED;
-
- if (state != STOP) {
- // If there are queued tasks but no threads, create
- // replacement.
- Runnable r = workQueue.poll();
- if (r != null) {
- addThread(r).start();
- return;
- }
-
- // If there are some (presumably delayed) tasks but
- // none pollable, create an idle replacement to wait.
- if (!workQueue.isEmpty()) {
- addThread(null).start();
- return;
- }
-
- // Otherwise, we can exit without replacement
- if (state == RUNNING)
- return;
- }
-
- // Either state is STOP, or state is SHUTDOWN and there is
- // no work to do. So we can terminate.
- termination.signalAll();
- runState = TERMINATED;
- // fall through to call terminate() outside of lock.
} finally {
mainLock.unlock();
}
- assert runState == TERMINATED;
- terminated();
+ tryTerminate();
+
+ int c = ctl.get();
+ if (runStateLessThan(c, STOP)) {
+ if (!completedAbruptly) {
+ int min = corePoolSize;
+ if (min == 0 && ! workQueue.isEmpty())
+ min = 1;
+ if (workerCountOf(c) >= min)
+ return; // replacement not needed
+ }
+ addWorker(null, false);
+ }
}
/**
- * Worker threads
+ * Performs blocking or timed wait for a task, depending on
+ * current configuration settings, or returns null if this worker
+ * must exit because of any of:
+ * 1. There are more than maximumPoolSize workers (due to
+ * a call to setMaximumPoolSize).
+ * 2. The pool is stopped.
+ * 3. The pool is shutdown and the queue is empty.
+ * 4. This worker timed out waiting for a task, and timed-out
+ * workers are subject to termination (that is,
+ * {@code workerCount > corePoolSize})
+ * both before and after the timed wait.
+ *
+ * @return task, or null if the worker must exit, in which case
+ * workerCount is decremented
*/
- private class Worker implements Runnable {
+ private Runnable getTask() {
+ boolean timedOut = false; // Did the last poll() time out?
- /**
- * The runLock is acquired and released surrounding each task
- * execution. It mainly protects against interrupts that are
- * intended to cancel the worker thread from instead
- * interrupting the task being run.
- */
- private final ReentrantLock runLock = new ReentrantLock();
-
- /**
- * Initial task to run before entering run loop
- */
- private Runnable firstTask;
-
- /**
- * Per thread completed task counter; accumulated
- * into completedTaskCount upon termination.
- */
- volatile long completedTasks;
+ retry:
+ for (;;) {
+ int c = ctl.get();
+ int rs = runStateOf(c);
- /**
- * Thread this worker is running in. Acts as a final field,
- * but cannot be set until thread is created.
- */
- Thread thread;
+ // Check if queue empty only if necessary.
+ if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
+ decrementWorkerCount();
+ return null;
+ }
- Worker(Runnable firstTask) {
- this.firstTask = firstTask;
- }
+ boolean timed; // Are workers subject to culling?
- boolean isActive() {
- return runLock.isLocked();
- }
+ for (;;) {
+ int wc = workerCountOf(c);
+ timed = wc > corePoolSize;
- /**
- * Interrupt thread if not running a task
- */
- void interruptIfIdle() {
- final ReentrantLock runLock = this.runLock;
- if (runLock.tryLock()) {
- try {
- thread.interrupt();
- } finally {
- runLock.unlock();
- }
+ if (wc <= maximumPoolSize && ! (timedOut && timed))
+ break;
+ if (compareAndDecrementWorkerCount(c))
+ return null;
+ c = ctl.get(); // Re-read ctl
+ if (runStateOf(c) != rs)
+ continue retry;
+ // else CAS failed due to workerCount change; retry inner loop
}
- }
-
- /**
- * Cause thread to die even if running a task.
- */
- void interruptNow() {
- thread.interrupt();
- }
- /**
- * Run a single task between before/after methods.
- */
- private void runTask(Runnable task) {
- final ReentrantLock runLock = this.runLock;
- runLock.lock();
try {
- // Abort now if immediate cancel. Otherwise, we have
- // committed to run this task.
- if (runState == STOP)
- return;
-
- Thread.interrupted(); // clear interrupt status on entry
- boolean ran = false;
- beforeExecute(thread, task);
- try {
- task.run();
- ran = true;
- afterExecute(task, null);
- ++completedTasks;
- } catch(RuntimeException ex) {
- if (!ran)
- afterExecute(task, ex);
- // Else the exception occurred within
- // afterExecute itself in which case we don't
- // want to call it again.
- throw ex;
- }
- } finally {
- runLock.unlock();
+ Runnable r = timed ?
+ workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
+ workQueue.take();
+ if (r != null)
+ return r;
+ timedOut = true;
+ } catch (InterruptedException retry) {
+ timedOut = false;
}
}
+ }
- /**
- * Main run loop
- */
- public void run() {
- try {
- Runnable task = firstTask;
- firstTask = null;
- while (task != null || (task = getTask()) != null) {
- runTask(task);
- task = null; // unnecessary but can help GC
+ /**
+ * Main worker run loop. Repeatedly gets tasks from queue and
+ * executes them, while coping with a number of issues:
+ *
+ * 1. We may start out with an initial task, in which case we
+ * don't need to get the first one. Otherwise, as long as pool is
+ * running, we get tasks from getTask. If it returns null then the
+ * worker exits due to changed pool state or configuration
+ * parameters. Other exits result from exception throws in
+ * external code, in which case completedAbruptly holds, which
+ * usually leads processWorkerExit to replace this thread.
+ *
+ * 2. Before running any task, the lock is acquired to prevent
+ * other pool interrupts while the task is executing, and
+ * clearInterruptsForTaskRun called to ensure that unless pool is
+ * stopping, this thread does not have its interrupt set.
+ *
+ * 3. Each task run is preceded by a call to beforeExecute, which
+ * might throw an exception, in which case we cause thread to die
+ * (breaking loop with completedAbruptly true) without processing
+ * the task.
+ *
+ * 4. Assuming beforeExecute completes normally, we run the task,
+ * gathering any of its thrown exceptions to send to
+ * afterExecute. We separately handle RuntimeException, Error
+ * (both of which the specs guarantee that we trap) and arbitrary
+ * Throwables. Because we cannot rethrow Throwables within
+ * Runnable.run, we wrap them within Errors on the way out (to the
+ * thread's UncaughtExceptionHandler). Any thrown exception also
+ * conservatively causes thread to die.
+ *
+ * 5. After task.run completes, we call afterExecute, which may
+ * also throw an exception, which will also cause thread to
+ * die. According to JLS Sec 14.20, this exception is the one that
+ * will be in effect even if task.run throws.
+ *
+ * The net effect of the exception mechanics is that afterExecute
+ * and the thread's UncaughtExceptionHandler have as accurate
+ * information as we can provide about any problems encountered by
+ * user code.
+ *
+ * @param w the worker
+ */
+ final void runWorker(Worker w) {
+ Runnable task = w.firstTask;
+ w.firstTask = null;
+ boolean completedAbruptly = true;
+ try {
+ while (task != null || (task = getTask()) != null) {
+ w.lock();
+ clearInterruptsForTaskRun();
+ try {
+ beforeExecute(w.thread, task);
+ Throwable thrown = null;
+ try {
+ task.run();
+ } catch (RuntimeException x) {
+ thrown = x; throw x;
+ } catch (Error x) {
+ thrown = x; throw x;
+ } catch (Throwable x) {
+ thrown = x; throw new Error(x);
+ } finally {
+ afterExecute(task, thrown);
+ }
+ } finally {
+ task = null;
+ w.completedTasks++;
+ w.unlock();
}
- } catch(InterruptedException ie) {
- // fall through
- } finally {
- workerDone(this);
}
+ completedAbruptly = false;
+ } finally {
+ processWorkerExit(w, completedAbruptly);
}
}
- // Public methods
+ // Public constructors and methods
/**
- * Creates a new <tt>ThreadPoolExecutor</tt> with the given
- * initial parameters and default thread factory and handler. It
- * may be more convenient to use one of the {@link Executors}
- * factory methods instead of this general purpose constructor.
+ * Creates a new {@code ThreadPoolExecutor} with the given initial
+ * parameters and default thread factory and rejected execution handler.
+ * It may be more convenient to use one of the {@link Executors} factory
+ * methods instead of this general purpose constructor.
*
- * @param corePoolSize the number of threads to keep in the
- * pool, even if they are idle.
+ * @param corePoolSize the number of threads to keep in the pool, even
+ * if they are idle
* @param maximumPoolSize the maximum number of threads to allow in the
- * pool.
+ * pool
* @param keepAliveTime when the number of threads is greater than
- * the core, this is the maximum time that excess idle threads
- * will wait for new tasks before terminating.
- * @param unit the time unit for the keepAliveTime
- * argument.
- * @param workQueue the queue to use for holding tasks before they
- * are executed. This queue will hold only the <tt>Runnable</tt>
- * tasks submitted by the <tt>execute</tt> method.
- * @throws IllegalArgumentException if corePoolSize, or
- * keepAliveTime less than zero, or if maximumPoolSize less than or
- * equal to zero, or if corePoolSize greater than maximumPoolSize.
- * @throws NullPointerException if <tt>workQueue</tt> is null
+ * the core, this is the maximum time that excess idle threads
+ * will wait for new tasks before terminating.
+ * @param unit the time unit for the {@code keepAliveTime} argument
+ * @param workQueue the queue to use for holding tasks before they are
+ * executed. This queue will hold only the {@code Runnable}
+ * tasks submitted by the {@code execute} method.
+ * @throws IllegalArgumentException if one of the following holds:<br>
+ * {@code corePoolSize < 0}<br>
+ * {@code keepAliveTime < 0}<br>
+ * {@code maximumPoolSize <= 0}<br>
+ * {@code maximumPoolSize < corePoolSize}
+ * @throws NullPointerException if {@code workQueue} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
@@ -716,28 +1123,29 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
}
/**
- * Creates a new <tt>ThreadPoolExecutor</tt> with the given initial
- * parameters.
+ * Creates a new {@code ThreadPoolExecutor} with the given initial
+ * parameters and default rejected execution handler.
*
- * @param corePoolSize the number of threads to keep in the
- * pool, even if they are idle.
+ * @param corePoolSize the number of threads to keep in the pool, even
+ * if they are idle
* @param maximumPoolSize the maximum number of threads to allow in the
- * pool.
+ * pool
* @param keepAliveTime when the number of threads is greater than
- * the core, this is the maximum time that excess idle threads
- * will wait for new tasks before terminating.
- * @param unit the time unit for the keepAliveTime
- * argument.
- * @param workQueue the queue to use for holding tasks before they
- * are executed. This queue will hold only the <tt>Runnable</tt>
- * tasks submitted by the <tt>execute</tt> method.
+ * the core, this is the maximum time that excess idle threads
+ * will wait for new tasks before terminating.
+ * @param unit the time unit for the {@code keepAliveTime} argument
+ * @param workQueue the queue to use for holding tasks before they are
+ * executed. This queue will hold only the {@code Runnable}
+ * tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
- * creates a new thread.
- * @throws IllegalArgumentException if corePoolSize, or
- * keepAliveTime less than zero, or if maximumPoolSize less than or
- * equal to zero, or if corePoolSize greater than maximumPoolSize.
- * @throws NullPointerException if <tt>workQueue</tt>
- * or <tt>threadFactory</tt> are null.
+ * creates a new thread
+ * @throws IllegalArgumentException if one of the following holds:<br>
+ * {@code corePoolSize < 0}<br>
+ * {@code keepAliveTime < 0}<br>
+ * {@code maximumPoolSize <= 0}<br>
+ * {@code maximumPoolSize < corePoolSize}
+ * @throws NullPointerException if {@code workQueue}
+ * or {@code threadFactory} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
@@ -750,28 +1158,29 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
}
/**
- * Creates a new <tt>ThreadPoolExecutor</tt> with the given initial
- * parameters.
+ * Creates a new {@code ThreadPoolExecutor} with the given initial
+ * parameters and default thread factory.
*
- * @param corePoolSize the number of threads to keep in the
- * pool, even if they are idle.
+ * @param corePoolSize the number of threads to keep in the pool, even
+ * if they are idle
* @param maximumPoolSize the maximum number of threads to allow in the
- * pool.
+ * pool
* @param keepAliveTime when the number of threads is greater than
- * the core, this is the maximum time that excess idle threads
- * will wait for new tasks before terminating.
- * @param unit the time unit for the keepAliveTime
- * argument.
- * @param workQueue the queue to use for holding tasks before they
- * are executed. This queue will hold only the <tt>Runnable</tt>
- * tasks submitted by the <tt>execute</tt> method.
+ * the core, this is the maximum time that excess idle threads
+ * will wait for new tasks before terminating.
+ * @param unit the time unit for the {@code keepAliveTime} argument
+ * @param workQueue the queue to use for holding tasks before they are
+ * executed. This queue will hold only the {@code Runnable}
+ * tasks submitted by the {@code execute} method.
* @param handler the handler to use when execution is blocked
- * because the thread bounds and queue capacities are reached.
- * @throws IllegalArgumentException if corePoolSize, or
- * keepAliveTime less than zero, or if maximumPoolSize less than or
- * equal to zero, or if corePoolSize greater than maximumPoolSize.
- * @throws NullPointerException if <tt>workQueue</tt>
- * or <tt>handler</tt> are null.
+ * because the thread bounds and queue capacities are reached
+ * @throws IllegalArgumentException if one of the following holds:<br>
+ * {@code corePoolSize < 0}<br>
+ * {@code keepAliveTime < 0}<br>
+ * {@code maximumPoolSize <= 0}<br>
+ * {@code maximumPoolSize < corePoolSize}
+ * @throws NullPointerException if {@code workQueue}
+ * or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
@@ -784,30 +1193,31 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
}
/**
- * Creates a new <tt>ThreadPoolExecutor</tt> with the given initial
+ * Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
- * @param corePoolSize the number of threads to keep in the
- * pool, even if they are idle.
+ * @param corePoolSize the number of threads to keep in the pool, even
+ * if they are idle
* @param maximumPoolSize the maximum number of threads to allow in the
- * pool.
+ * pool
* @param keepAliveTime when the number of threads is greater than
- * the core, this is the maximum time that excess idle threads
- * will wait for new tasks before terminating.
- * @param unit the time unit for the keepAliveTime
- * argument.
- * @param workQueue the queue to use for holding tasks before they
- * are executed. This queue will hold only the <tt>Runnable</tt>
- * tasks submitted by the <tt>execute</tt> method.
+ * the core, this is the maximum time that excess idle threads
+ * will wait for new tasks before terminating.
+ * @param unit the time unit for the {@code keepAliveTime} argument
+ * @param workQueue the queue to use for holding tasks before they are
+ * executed. This queue will hold only the {@code Runnable}
+ * tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
- * creates a new thread.
+ * creates a new thread
* @param handler the handler to use when execution is blocked
- * because the thread bounds and queue capacities are reached.
- * @throws IllegalArgumentException if corePoolSize, or
- * keepAliveTime less than zero, or if maximumPoolSize less than or
- * equal to zero, or if corePoolSize greater than maximumPoolSize.
- * @throws NullPointerException if <tt>workQueue</tt>
- * or <tt>threadFactory</tt> or <tt>handler</tt> are null.
+ * because the thread bounds and queue capacities are reached
+ * @throws IllegalArgumentException if one of the following holds:<br>
+ * {@code corePoolSize < 0}<br>
+ * {@code keepAliveTime < 0}<br>
+ * {@code maximumPoolSize <= 0}<br>
+ * {@code maximumPoolSize < corePoolSize}
+ * @throws NullPointerException if {@code workQueue}
+ * or {@code threadFactory} or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
@@ -831,181 +1241,140 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
this.handler = handler;
}
-
/**
* Executes the given task sometime in the future. The task
* may execute in a new thread or in an existing pooled thread.
*
* If the task cannot be submitted for execution, either because this
* executor has been shutdown or because its capacity has been reached,
- * the task is handled by the current <tt>RejectedExecutionHandler</tt>.
+ * the task is handled by the current {@code RejectedExecutionHandler}.
*
* @param command the task to execute
* @throws RejectedExecutionException at discretion of
- * <tt>RejectedExecutionHandler</tt>, if task cannot be accepted
- * for execution
- * @throws NullPointerException if command is null
+ * {@code RejectedExecutionHandler}, if the task
+ * cannot be accepted for execution
+ * @throws NullPointerException if {@code command} is null
*/
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
- for (;;) {
- if (runState != RUNNING) {
- reject(command);
- return;
- }
- if (poolSize < corePoolSize && addIfUnderCorePoolSize(command))
- return;
- if (workQueue.offer(command))
- return;
- Runnable r = addIfUnderMaximumPoolSize(command);
- if (r == command)
+ /*
+ * Proceed in 3 steps:
+ *
+ * 1. If fewer than corePoolSize threads are running, try to
+ * start a new thread with the given command as its first
+ * task. The call to addWorker atomically checks runState and
+ * workerCount, and so prevents false alarms that would add
+ * threads when it shouldn't, by returning false.
+ *
+ * 2. If a task can be successfully queued, then we still need
+ * to double-check whether we should have added a thread
+ * (because existing ones died since last checking) or that
+ * the pool shut down since entry into this method. So we
+ * recheck state and if necessary roll back the enqueuing if
+ * stopped, or start a new thread if there are none.
+ *
+ * 3. If we cannot queue task, then we try to add a new
+ * thread. If it fails, we know we are shut down or saturated
+ * and so reject the task.
+ */
+ int c = ctl.get();
+ if (workerCountOf(c) < corePoolSize) {
+ if (addWorker(command, true))
return;
- if (r == null) {
+ c = ctl.get();
+ }
+ if (isRunning(c) && workQueue.offer(command)) {
+ int recheck = ctl.get();
+ if (! isRunning(recheck) && remove(command))
reject(command);
- return;
- }
- // else retry
+ else if (workerCountOf(recheck) == 0)
+ addWorker(null, false);
}
+ else if (!addWorker(command, false))
+ reject(command);
}
/**
* Initiates an orderly shutdown in which previously submitted
- * tasks are executed, but no new tasks will be
- * accepted. Invocation has no additional effect if already shut
- * down.
- * @throws SecurityException if a security manager exists and
- * shutting down this ExecutorService may manipulate threads that
- * the caller is not permitted to modify because it does not hold
- * {@link java.lang.RuntimePermission}<tt>("modifyThread")</tt>,
- * or the security manager's <tt>checkAccess</tt> method denies access.
+ * tasks are executed, but no new tasks will be accepted.
+ * Invocation has no additional effect if already shut down.
+ *
+ * <p>This method does not wait for previously submitted tasks to
+ * complete execution. Use {@link #awaitTermination awaitTermination}
+ * to do that.
+ *
+ * @throws SecurityException {@inheritDoc}
*/
public void shutdown() {
- // Fail if caller doesn't have modifyThread permission
- SecurityManager security = System.getSecurityManager();
- if (security != null)
- java.security.AccessController.checkPermission(shutdownPerm);
-
- boolean fullyTerminated = false;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
- if (workers.size() > 0) {
- // Check if caller can modify worker threads. This
- // might not be true even if passed above check, if
- // the SecurityManager treats some threads specially.
- if (security != null) {
- for (Worker w: workers)
- security.checkAccess(w.thread);
- }
-
- int state = runState;
- if (state == RUNNING) // don't override shutdownNow
- runState = SHUTDOWN;
-
- try {
- for (Worker w: workers)
- w.interruptIfIdle();
- } catch(SecurityException se) {
- // If SecurityManager allows above checks, but
- // then unexpectedly throws exception when
- // interrupting threads (which it ought not do),
- // back out as cleanly as we can. Some threads may
- // have been killed but we remain in non-shutdown
- // state.
- runState = state;
- throw se;
- }
- }
- else { // If no workers, trigger full termination now
- fullyTerminated = true;
- runState = TERMINATED;
- termination.signalAll();
- }
+ checkShutdownAccess();
+ advanceRunState(SHUTDOWN);
+ interruptIdleWorkers();
+ onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
- if (fullyTerminated)
- terminated();
+ tryTerminate();
}
-
/**
* Attempts to stop all actively executing tasks, halts the
- * processing of waiting tasks, and returns a list of the tasks that were
- * awaiting execution.
- *
- * <p>This implementation cancels tasks via {@link
- * Thread#interrupt}, so if any tasks mask or fail to respond to
- * interrupts, they may never terminate.
+ * processing of waiting tasks, and returns a list of the tasks
+ * that were awaiting execution. These tasks are drained (removed)
+ * from the task queue upon return from this method.
*
- * @return list of tasks that never commenced execution
- * @throws SecurityException if a security manager exists and
- * shutting down this ExecutorService may manipulate threads that
- * the caller is not permitted to modify because it does not hold
- * {@link java.lang.RuntimePermission}<tt>("modifyThread")</tt>,
- * or the security manager's <tt>checkAccess</tt> method denies access.
+ * <p>This method does not wait for actively executing tasks to
+ * terminate. Use {@link #awaitTermination awaitTermination} to
+ * do that.
+ *
+ * <p>There are no guarantees beyond best-effort attempts to stop
+ * processing actively executing tasks. This implementation
+ * cancels tasks via {@link Thread#interrupt}, so any task that
+ * fails to respond to interrupts may never terminate.
+ *
+ * @throws SecurityException {@inheritDoc}
*/
public List<Runnable> shutdownNow() {
- // Almost the same code as shutdown()
- SecurityManager security = System.getSecurityManager();
- if (security != null)
- java.security.AccessController.checkPermission(shutdownPerm);
-
- boolean fullyTerminated = false;
+ List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
- if (workers.size() > 0) {
- if (security != null) {
- for (Worker w: workers)
- security.checkAccess(w.thread);
- }
-
- int state = runState;
- if (state != TERMINATED)
- runState = STOP;
- try {
- for (Worker w : workers)
- w.interruptNow();
- } catch(SecurityException se) {
- runState = state; // back out;
- throw se;
- }
- }
- else { // If no workers, trigger full termination now
- fullyTerminated = true;
- runState = TERMINATED;
- termination.signalAll();
- }
+ checkShutdownAccess();
+ advanceRunState(STOP);
+ interruptWorkers();
+ tasks = drainQueue();
} finally {
mainLock.unlock();
}
- if (fullyTerminated)
- terminated();
- return Arrays.asList(workQueue.toArray(EMPTY_RUNNABLE_ARRAY));
+ tryTerminate();
+ return tasks;
}
public boolean isShutdown() {
- return runState != RUNNING;
+ return ! isRunning(ctl.get());
}
- /**
+ /**
* Returns true if this executor is in the process of terminating
- * after <tt>shutdown</tt> or <tt>shutdownNow</tt> but has not
+ * after {@link #shutdown} or {@link #shutdownNow} but has not
* completely terminated. This method may be useful for
- * debugging. A return of <tt>true</tt> reported a sufficient
+ * debugging. A return of {@code true} reported a sufficient
* period after shutdown may indicate that submitted tasks have
* ignored or suppressed interruption, causing this executor not
* to properly terminate.
- * @return true if terminating but not yet terminated.
+ *
+ * @return true if terminating but not yet terminated
*/
public boolean isTerminating() {
- return runState == STOP;
+ int c = ctl.get();
+ return ! isRunning(c) && runStateLessThan(c, TERMINATED);
}
public boolean isTerminated() {
- return runState == TERMINATED;
+ return runStateAtLeast(ctl.get(), TERMINATED);
}
public boolean awaitTermination(long timeout, TimeUnit unit)
@@ -1015,7 +1384,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
mainLock.lock();
try {
for (;;) {
- if (runState == TERMINATED)
+ if (runStateAtLeast(ctl.get(), TERMINATED))
return true;
if (nanos <= 0)
return false;
@@ -1027,10 +1396,10 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
}
/**
- * Invokes <tt>shutdown</tt> when this executor is no longer
- * referenced.
- */
- protected void finalize() {
+ * Invokes {@code shutdown} when this executor is no longer
+ * referenced and it has no threads.
+ */
+ protected void finalize() {
shutdown();
}
@@ -1081,103 +1450,33 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
}
/**
- * Returns the task queue used by this executor. Access to the
- * task queue is intended primarily for debugging and monitoring.
- * This queue may be in active use. Retrieving the task queue
- * does not prevent queued tasks from executing.
- *
- * @return the task queue
- */
- public BlockingQueue<Runnable> getQueue() {
- return workQueue;
- }
-
- /**
- * Removes this task from the executor's internal queue if it is
- * present, thus causing it not to be run if it has not already
- * started.
- *
- * <p> This method may be useful as one part of a cancellation
- * scheme. It may fail to remove tasks that have been converted
- * into other forms before being placed on the internal queue. For
- * example, a task entered using <tt>submit</tt> might be
- * converted into a form that maintains <tt>Future</tt> status.
- * However, in such cases, method {@link ThreadPoolExecutor#purge}
- * may be used to remove those Futures that have been cancelled.
- *
- *
- * @param task the task to remove
- * @return true if the task was removed
- */
- public boolean remove(Runnable task) {
- return getQueue().remove(task);
- }
-
-
- /**
- * Tries to remove from the work queue all {@link Future}
- * tasks that have been cancelled. This method can be useful as a
- * storage reclamation operation, that has no other impact on
- * functionality. Cancelled tasks are never executed, but may
- * accumulate in work queues until worker threads can actively
- * remove them. Invoking this method instead tries to remove them now.
- * However, this method may fail to remove tasks in
- * the presence of interference by other threads.
- */
- public void purge() {
- // Fail if we encounter interference during traversal
- try {
- Iterator<Runnable> it = getQueue().iterator();
- while (it.hasNext()) {
- Runnable r = it.next();
- if (r instanceof Future<?>) {
- Future<?> c = (Future<?>)r;
- if (c.isCancelled())
- it.remove();
- }
- }
- }
- catch(ConcurrentModificationException ex) {
- return;
- }
- }
-
- /**
* Sets the core number of threads. This overrides any value set
* in the constructor. If the new value is smaller than the
* current value, excess existing threads will be terminated when
- * they next become idle. If larger, new threads will, if needed,
+ * they next become idle. If larger, new threads will, if needed,
* be started to execute any queued tasks.
*
* @param corePoolSize the new core size
- * @throws IllegalArgumentException if <tt>corePoolSize</tt>
- * less than zero
+ * @throws IllegalArgumentException if {@code corePoolSize < 0}
* @see #getCorePoolSize
*/
public void setCorePoolSize(int corePoolSize) {
if (corePoolSize < 0)
throw new IllegalArgumentException();
- final ReentrantLock mainLock = this.mainLock;
- mainLock.lock();
- try {
- int extra = this.corePoolSize - corePoolSize;
- this.corePoolSize = corePoolSize;
- if (extra < 0) {
- Runnable r;
- while (extra++ < 0 && poolSize < corePoolSize &&
- (r = workQueue.poll()) != null)
- addThread(r).start();
- }
- else if (extra > 0 && poolSize > corePoolSize) {
- Iterator<Worker> it = workers.iterator();
- while (it.hasNext() &&
- extra-- > 0 &&
- poolSize > corePoolSize &&
- workQueue.remainingCapacity() == 0)
- it.next().interruptIfIdle();
+ int delta = corePoolSize - this.corePoolSize;
+ this.corePoolSize = corePoolSize;
+ if (workerCountOf(ctl.get()) > corePoolSize)
+ interruptIdleWorkers();
+ else if (delta > 0) {
+ // We don't really know how many new threads are "needed".
+ // As a heuristic, prestart enough new workers (up to new
+ // core size) to handle the current number of tasks in
+ // queue, but stop if queue becomes empty while doing so.
+ int k = Math.min(delta, workQueue.size());
+ while (k-- > 0 && addWorker(null, true)) {
+ if (workQueue.isEmpty())
+ break;
}
- } finally {
- mainLock.unlock();
}
}
@@ -1194,23 +1493,26 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
/**
* Starts a core thread, causing it to idly wait for work. This
* overrides the default policy of starting core threads only when
- * new tasks are executed. This method will return <tt>false</tt>
+ * new tasks are executed. This method will return {@code false}
* if all core threads have already been started.
- * @return true if a thread was started
- */
+ *
+ * @return {@code true} if a thread was started
+ */
public boolean prestartCoreThread() {
- return addIfUnderCorePoolSize(null);
+ return workerCountOf(ctl.get()) < corePoolSize &&
+ addWorker(null, true);
}
/**
* Starts all core threads, causing them to idly wait for work. This
* overrides the default policy of starting core threads only when
- * new tasks are executed.
- * @return the number of threads started.
- */
+ * new tasks are executed.
+ *
+ * @return the number of threads started
+ */
public int prestartAllCoreThreads() {
int n = 0;
- while (addIfUnderCorePoolSize(null))
+ while (addWorker(null, true))
++n;
return n;
}
@@ -1222,30 +1524,17 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
* terminated when they next become idle.
*
* @param maximumPoolSize the new maximum
- * @throws IllegalArgumentException if maximumPoolSize less than zero or
- * the {@link #getCorePoolSize core pool size}
+ * @throws IllegalArgumentException if the new maximum is
+ * less than or equal to zero, or
+ * less than the {@linkplain #getCorePoolSize core pool size}
* @see #getMaximumPoolSize
*/
public void setMaximumPoolSize(int maximumPoolSize) {
if (maximumPoolSize <= 0 || maximumPoolSize < corePoolSize)
throw new IllegalArgumentException();
- final ReentrantLock mainLock = this.mainLock;
- mainLock.lock();
- try {
- int extra = this.maximumPoolSize - maximumPoolSize;
- this.maximumPoolSize = maximumPoolSize;
- if (extra > 0 && poolSize > maximumPoolSize) {
- Iterator<Worker> it = workers.iterator();
- while (it.hasNext() &&
- extra > 0 &&
- poolSize > maximumPoolSize) {
- it.next().interruptIfIdle();
- --extra;
- }
- }
- } finally {
- mainLock.unlock();
- }
+ this.maximumPoolSize = maximumPoolSize;
+ if (workerCountOf(ctl.get()) > maximumPoolSize)
+ interruptIdleWorkers();
}
/**
@@ -1264,21 +1553,27 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
* threads currently in the pool, after waiting this amount of
* time without processing a task, excess threads will be
* terminated. This overrides any value set in the constructor.
+ *
* @param time the time to wait. A time value of zero will cause
- * excess threads to terminate immediately after executing tasks.
- * @param unit the time unit of the time argument
- * @throws IllegalArgumentException if time less than zero
+ * excess threads to terminate immediately after executing tasks.
+ * @param unit the time unit of the {@code time} argument
+ * @throws IllegalArgumentException if {@code time} less than zero or
+ * if {@code time} is zero and {@code allowsCoreThreadTimeOut}
* @see #getKeepAliveTime
*/
public void setKeepAliveTime(long time, TimeUnit unit) {
if (time < 0)
throw new IllegalArgumentException();
- this.keepAliveTime = unit.toNanos(time);
+ long keepAliveTime = unit.toNanos(time);
+ long delta = keepAliveTime - this.keepAliveTime;
+ this.keepAliveTime = keepAliveTime;
+ if (delta < 0)
+ interruptIdleWorkers();
}
/**
* Returns the thread keep-alive time, which is the amount of time
- * which threads in excess of the core pool size may remain
+ * that threads in excess of the core pool size may remain
* idle before being terminated.
*
* @param unit the desired time unit of the result
@@ -1289,6 +1584,73 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
return unit.convert(keepAliveTime, TimeUnit.NANOSECONDS);
}
+ /* User-level queue utilities */
+
+ /**
+ * Returns the task queue used by this executor. Access to the
+ * task queue is intended primarily for debugging and monitoring.
+ * This queue may be in active use. Retrieving the task queue
+ * does not prevent queued tasks from executing.
+ *
+ * @return the task queue
+ */
+ public BlockingQueue<Runnable> getQueue() {
+ return workQueue;
+ }
+
+ /**
+ * Removes this task from the executor's internal queue if it is
+ * present, thus causing it not to be run if it has not already
+ * started.
+ *
+ * <p> This method may be useful as one part of a cancellation
+ * scheme. It may fail to remove tasks that have been converted
+ * into other forms before being placed on the internal queue. For
+ * example, a task entered using {@code submit} might be
+ * converted into a form that maintains {@code Future} status.
+ * However, in such cases, method {@link #purge} may be used to
+ * remove those Futures that have been cancelled.
+ *
+ * @param task the task to remove
+ * @return true if the task was removed
+ */
+ public boolean remove(Runnable task) {
+ boolean removed = workQueue.remove(task);
+ tryTerminate(); // In case SHUTDOWN and now empty
+ return removed;
+ }
+
+ /**
+ * Tries to remove from the work queue all {@link Future}
+ * tasks that have been cancelled. This method can be useful as a
+ * storage reclamation operation, that has no other impact on
+ * functionality. Cancelled tasks are never executed, but may
+ * accumulate in work queues until worker threads can actively
+ * remove them. Invoking this method instead tries to remove them now.
+ * However, this method may fail to remove tasks in
+ * the presence of interference by other threads.
+ */
+ public void purge() {
+ final BlockingQueue<Runnable> q = workQueue;
+ try {
+ Iterator<Runnable> it = q.iterator();
+ while (it.hasNext()) {
+ Runnable r = it.next();
+ if (r instanceof Future<?> && ((Future<?>)r).isCancelled())
+ it.remove();
+ }
+ } catch (ConcurrentModificationException fallThrough) {
+ // Take slow path if we encounter interference during traversal.
+ // Make copy for traversal and call remove for cancelled entries.
+ // The slow path is more likely to be O(N*N).
+ for (Object r : q.toArray())
+ if (r instanceof Future<?> && ((Future<?>)r).isCancelled())
+ q.remove(r);
+ }
+
+ tryTerminate(); // In case SHUTDOWN and now empty
+ }
+
/* Statistics */
/**
@@ -1297,7 +1659,16 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
* @return the number of threads
*/
public int getPoolSize() {
- return poolSize;
+ final ReentrantLock mainLock = this.mainLock;
+ mainLock.lock();
+ try {
+ // Remove rare and surprising possibility of
+ // isTerminated() && getPoolSize() > 0
+ return runStateAtLeast(ctl.get(), TIDYING) ? 0
+ : workers.size();
+ } finally {
+ mainLock.unlock();
+ }
}
/**
@@ -1311,10 +1682,9 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
mainLock.lock();
try {
int n = 0;
- for (Worker w : workers) {
- if (w.isActive())
+ for (Worker w : workers)
+ if (w.isLocked())
++n;
- }
return n;
} finally {
mainLock.unlock();
@@ -1338,11 +1708,10 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
}
/**
- * Returns the approximate total number of tasks that have been
+ * Returns the approximate total number of tasks that have ever been
* scheduled for execution. Because the states of tasks and
* threads may change dynamically during computation, the returned
- * value is only an approximation, but one that does not ever
- * decrease across successive calls.
+ * value is only an approximation.
*
* @return the number of tasks
*/
@@ -1353,7 +1722,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
long n = completedTaskCount;
for (Worker w : workers) {
n += w.completedTasks;
- if (w.isActive())
+ if (w.isLocked())
++n;
}
return n + workQueue.size();
@@ -1384,30 +1753,69 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
}
}
+ /* Extension hooks */
+
/**
* Method invoked prior to executing the given Runnable in the
- * given thread. This method is invoked by thread <tt>t</tt> that
- * will execute task <tt>r</tt>, and may be used to re-initialize
- * ThreadLocals, or to perform logging. Note: To properly nest
- * multiple overridings, subclasses should generally invoke
- * <tt>super.beforeExecute</tt> at the end of this method.
+ * given thread. This method is invoked by thread {@code t} that
+ * will execute task {@code r}, and may be used to re-initialize
+ * ThreadLocals, or to perform logging.
+ *
+ * <p>This implementation does nothing, but may be customized in
+ * subclasses. Note: To properly nest multiple overridings, subclasses
+ * should generally invoke {@code super.beforeExecute} at the end of
+ * this method.
*
- * @param t the thread that will run task r.
- * @param r the task that will be executed.
+ * @param t the thread that will run task {@code r}
+ * @param r the task that will be executed
*/
protected void beforeExecute(Thread t, Runnable r) { }
/**
- * Method invoked upon completion of execution of the given
- * Runnable. This method is invoked by the thread that executed
- * the task. If non-null, the Throwable is the uncaught exception
- * that caused execution to terminate abruptly. Note: To properly
- * nest multiple overridings, subclasses should generally invoke
- * <tt>super.afterExecute</tt> at the beginning of this method.
+ * Method invoked upon completion of execution of the given Runnable.
+ * This method is invoked by the thread that executed the task. If
+ * non-null, the Throwable is the uncaught {@code RuntimeException}
+ * or {@code Error} that caused execution to terminate abruptly.
+ *
+ * <p>This implementation does nothing, but may be customized in
+ * subclasses. Note: To properly nest multiple overridings, subclasses
+ * should generally invoke {@code super.afterExecute} at the
+ * beginning of this method.
*
- * @param r the runnable that has completed.
+ * <p><b>Note:</b> When actions are enclosed in tasks (such as
+ * {@link FutureTask}) either explicitly or via methods such as
+ * {@code submit}, these task objects catch and maintain
+ * computational exceptions, and so they do not cause abrupt
+ * termination, and the internal exceptions are <em>not</em>
+ * passed to this method. If you would like to trap both kinds of
+ * failures in this method, you can further probe for such cases,
+ * as in this sample subclass that prints either the direct cause
+ * or the underlying exception if a task has been aborted:
+ *
+ * <pre> {@code
+ * class ExtendedExecutor extends ThreadPoolExecutor {
+ * // ...
+ * protected void afterExecute(Runnable r, Throwable t) {
+ * super.afterExecute(r, t);
+ * if (t == null && r instanceof Future<?>) {
+ * try {
+ * Object result = ((Future<?>) r).get();
+ * } catch (CancellationException ce) {
+ * t = ce;
+ * } catch (ExecutionException ee) {
+ * t = ee.getCause();
+ * } catch (InterruptedException ie) {
+ * Thread.currentThread().interrupt(); // ignore/reset
+ * }
+ * }
+ * if (t != null)
+ * System.out.println(t);
+ * }
+ * }}</pre>
+ *
+ * @param r the runnable that has completed
* @param t the exception that caused termination, or null if
- * execution completed normally.
+ * execution completed normally
*/
protected void afterExecute(Runnable r, Throwable t) { }
@@ -1415,25 +1823,28 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
* Method invoked when the Executor has terminated. Default
* implementation does nothing. Note: To properly nest multiple
* overridings, subclasses should generally invoke
- * <tt>super.terminated</tt> within this method.
+ * {@code super.terminated} within this method.
*/
protected void terminated() { }
+ /* Predefined RejectedExecutionHandlers */
+
/**
* A handler for rejected tasks that runs the rejected task
- * directly in the calling thread of the <tt>execute</tt> method,
+ * directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down, in which case the task
* is discarded.
*/
- public static class CallerRunsPolicy implements RejectedExecutionHandler {
+ public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
- * Creates a <tt>CallerRunsPolicy</tt>.
+ * Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
+ *
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
@@ -1446,16 +1857,17 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
/**
* A handler for rejected tasks that throws a
- * <tt>RejectedExecutionException</tt>.
+ * {@code RejectedExecutionException}.
*/
public static class AbortPolicy implements RejectedExecutionHandler {
/**
- * Creates an <tt>AbortPolicy</tt>.
+ * Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
+ *
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always.
@@ -1471,12 +1883,13 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
- * Creates a <tt>DiscardPolicy</tt>.
+ * Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
+ *
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
@@ -1486,12 +1899,12 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
/**
* A handler for rejected tasks that discards the oldest unhandled
- * request and then retries <tt>execute</tt>, unless the executor
+ * request and then retries {@code execute}, unless the executor
* is shut down, in which case the task is discarded.
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
- * Creates a <tt>DiscardOldestPolicy</tt> for the given executor.
+ * Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
@@ -1500,6 +1913,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
+ *
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
diff --git a/concurrent/src/main/java/java/util/concurrent/TimeUnit.java b/concurrent/src/main/java/java/util/concurrent/TimeUnit.java
index b186aeb..412d28a 100644
--- a/concurrent/src/main/java/java/util/concurrent/TimeUnit.java
+++ b/concurrent/src/main/java/java/util/concurrent/TimeUnit.java
@@ -12,7 +12,11 @@ package java.util.concurrent;
* and to perform timing and delay operations in these units. A
* <tt>TimeUnit</tt> does not maintain time information, but only
* helps organize and use time representations that may be maintained
- * separately across various contexts.
+ * separately across various contexts. A nanosecond is defined as one
+ * thousandth of a microsecond, a microsecond as one thousandth of a
+ * millisecond, a millisecond as one thousandth of a second, a minute
+ * as sixty seconds, an hour as sixty minutes, and a day as twenty four
+ * hours.
*
* <p>A <tt>TimeUnit</tt> is mainly used to inform time-based methods
* how a given timing parameter should be interpreted. For example,
@@ -37,61 +41,80 @@ package java.util.concurrent;
*/
public enum TimeUnit {
/** TimeUnit which represents one nanosecond. */
- NANOSECONDS(0),
+ NANOSECONDS {
+ public long toNanos(long d) { return d; }
+ public long toMicros(long d) { return d/(C1/C0); }
+ public long toMillis(long d) { return d/(C2/C0); }
+ public long toSeconds(long d) { return d/(C3/C0); }
+ public long toMinutes(long d) { return d/(C4/C0); }
+ public long toHours(long d) { return d/(C5/C0); }
+ public long toDays(long d) { return d/(C6/C0); }
+ public long convert(long d, TimeUnit u) { return u.toNanos(d); }
+ int excessNanos(long d, long m) { return (int)(d - (m*C2)); }
+ },
/** TimeUnit which represents one microsecond. */
- MICROSECONDS(1),
+ MICROSECONDS {
+ public long toNanos(long d) { return x(d, C1/C0, MAX/(C1/C0)); }
+ public long toMicros(long d) { return d; }
+ public long toMillis(long d) { return d/(C2/C1); }
+ public long toSeconds(long d) { return d/(C3/C1); }
+ public long toMinutes(long d) { return d/(C4/C1); }
+ public long toHours(long d) { return d/(C5/C1); }
+ public long toDays(long d) { return d/(C6/C1); }
+ public long convert(long d, TimeUnit u) { return u.toMicros(d); }
+ int excessNanos(long d, long m) { return (int)((d*C1) - (m*C2)); }
+ },
/** TimeUnit which represents one millisecond. */
- MILLISECONDS(2),
+ MILLISECONDS {
+ public long toNanos(long d) { return x(d, C2/C0, MAX/(C2/C0)); }
+ public long toMicros(long d) { return x(d, C2/C1, MAX/(C2/C1)); }
+ public long toMillis(long d) { return d; }
+ public long toSeconds(long d) { return d/(C3/C2); }
+ public long toMinutes(long d) { return d/(C4/C2); }
+ public long toHours(long d) { return d/(C5/C2); }
+ public long toDays(long d) { return d/(C6/C2); }
+ public long convert(long d, TimeUnit u) { return u.toMillis(d); }
+ int excessNanos(long d, long m) { return 0; }
+ },
/** TimeUnit which represents one second. */
- SECONDS(3);
+ SECONDS {
+ public long toNanos(long d) { return x(d, C3/C0, MAX/(C3/C0)); }
+ public long toMicros(long d) { return x(d, C3/C1, MAX/(C3/C1)); }
+ public long toMillis(long d) { return x(d, C3/C2, MAX/(C3/C2)); }
+ public long toSeconds(long d) { return d; }
+ public long toMinutes(long d) { return d/(C4/C3); }
+ public long toHours(long d) { return d/(C5/C3); }
+ public long toDays(long d) { return d/(C6/C3); }
+ public long convert(long d, TimeUnit u) { return u.toSeconds(d); }
+ int excessNanos(long d, long m) { return 0; }
+ };
- /** the index of this unit */
- private final int index;
+ // Handy constants for conversion methods
+ static final long C0 = 1L;
+ static final long C1 = C0 * 1000L;
+ static final long C2 = C1 * 1000L;
+ static final long C3 = C2 * 1000L;
+ static final long C4 = C3 * 60L;
+ static final long C5 = C4 * 60L;
+ static final long C6 = C5 * 24L;
- /** Internal constructor */
- TimeUnit(int index) {
- this.index = index;
- }
-
- /** Lookup table for conversion factors */
- private static final int[] multipliers = {
- 1,
- 1000,
- 1000 * 1000,
- 1000 * 1000 * 1000
- };
-
- /**
- * Lookup table to check saturation. Note that because we are
- * dividing these down, we don't have to deal with asymmetry of
- * MIN/MAX values.
- */
- private static final long[] overflows = {
- 0, // unused
- Long.MAX_VALUE / 1000,
- Long.MAX_VALUE / (1000 * 1000),
- Long.MAX_VALUE / (1000 * 1000 * 1000)
- };
+ static final long MAX = Long.MAX_VALUE;
/**
- * Perform conversion based on given delta representing the
- * difference between units
- * @param delta the difference in index values of source and target units
- * @param duration the duration
- * @return converted duration or saturated value
+ * Scale d by m, checking for overflow.
+ * This has a short name to make above code more readable.
*/
- private static long doConvert(int delta, long duration) {
- if (delta == 0)
- return duration;
- if (delta < 0)
- return duration / multipliers[-delta];
- if (duration > overflows[delta])
- return Long.MAX_VALUE;
- if (duration < -overflows[delta])
- return Long.MIN_VALUE;
- return duration * multipliers[delta];
+ static long x(long d, long m, long over) {
+ if (d > over) return Long.MAX_VALUE;
+ if (d < -over) return Long.MIN_VALUE;
+ return d * m;
}
+ // To maintain full signature compatibility with 1.5, and to improve the
+ // clarity of the generated javadoc (see 6287639: Abstract methods in
+ // enum classes should not be listed as abstract), method convert
+ // etc. are not declared abstract but otherwise act as abstract methods.
+
/**
* Convert the given time duration in the given unit to this
* unit. Conversions from finer to coarser granularities
@@ -102,14 +125,17 @@ public enum TimeUnit {
* <tt>Long.MIN_VALUE</tt> if negative or <tt>Long.MAX_VALUE</tt>
* if positive.
*
- * @param duration the time duration in the given <tt>unit</tt>
- * @param unit the unit of the <tt>duration</tt> argument
+ * <p>For example, to convert 10 minutes to milliseconds, use:
+ * <tt>TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES)</tt>
+ *
+ * @param sourceDuration the time duration in the given <tt>sourceUnit</tt>
+ * @param sourceUnit the unit of the <tt>sourceDuration</tt> argument
* @return the converted duration in this unit,
* or <tt>Long.MIN_VALUE</tt> if conversion would negatively
* overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow.
*/
- public long convert(long duration, TimeUnit unit) {
- return doConvert(unit.index - index, duration);
+ public long convert(long sourceDuration, TimeUnit sourceUnit) {
+ throw new AbstractMethodError();
}
/**
@@ -121,7 +147,7 @@ public enum TimeUnit {
* @see #convert
*/
public long toNanos(long duration) {
- return doConvert(index, duration);
+ throw new AbstractMethodError();
}
/**
@@ -133,7 +159,7 @@ public enum TimeUnit {
* @see #convert
*/
public long toMicros(long duration) {
- return doConvert(index - MICROSECONDS.index, duration);
+ throw new AbstractMethodError();
}
/**
@@ -145,34 +171,66 @@ public enum TimeUnit {
* @see #convert
*/
public long toMillis(long duration) {
- return doConvert(index - MILLISECONDS.index, duration);
+ throw new AbstractMethodError();
}
/**
* Equivalent to <tt>SECONDS.convert(duration, this)</tt>.
* @param duration the duration
- * @return the converted duration.
+ * @return the converted duration,
+ * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
+ * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow.
* @see #convert
*/
public long toSeconds(long duration) {
- return doConvert(index - SECONDS.index, duration);
+ throw new AbstractMethodError();
+ }
+
+ /**
+ * Equivalent to <tt>MINUTES.convert(duration, this)</tt>.
+ * @param duration the duration
+ * @return the converted duration,
+ * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
+ * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow.
+ * @see #convert
+ */
+ long toMinutes(long duration) {
+ throw new AbstractMethodError();
}
+ /**
+ * Equivalent to <tt>HOURS.convert(duration, this)</tt>.
+ * @param duration the duration
+ * @return the converted duration,
+ * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
+ * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow.
+ * @see #convert
+ */
+ long toHours(long duration) {
+ throw new AbstractMethodError();
+ }
/**
- * Utility method to compute the excess-nanosecond argument to
- * wait, sleep, join.
+ * Equivalent to <tt>DAYS.convert(duration, this)</tt>.
+ * @param duration the duration
+ * @return the converted duration
+ * @see #convert
*/
- private int excessNanos(long time, long ms) {
- if (this == NANOSECONDS)
- return (int) (time - (ms * 1000 * 1000));
- if (this == MICROSECONDS)
- return (int) ((time * 1000) - (ms * 1000 * 1000));
- return 0;
+ long toDays(long duration) {
+ throw new AbstractMethodError();
}
/**
- * Perform a timed <tt>Object.wait</tt> using this time unit.
+ * Utility to compute the excess-nanosecond argument to wait,
+ * sleep, join.
+ * @param d the duration
+ * @param m the number of milliseconds
+ * @return the number of nanoseconds
+ */
+ abstract int excessNanos(long d, long m);
+
+ /**
+ * Performs a timed <tt>Object.wait</tt> using this time unit.
* This is a convenience method that converts timeout arguments
* into the form required by the <tt>Object.wait</tt> method.
*
@@ -180,7 +238,7 @@ public enum TimeUnit {
* method (see {@link BlockingQueue#poll BlockingQueue.poll})
* using:
*
- * <pre> public synchronized Object poll(long timeout, TimeUnit unit) throws InterruptedException {
+ * <pre> public synchronized Object poll(long timeout, TimeUnit unit) throws InterruptedException {
* while (empty) {
* unit.timedWait(this, timeout);
* ...
@@ -188,12 +246,13 @@ public enum TimeUnit {
* }</pre>
*
* @param obj the object to wait on
- * @param timeout the maximum time to wait.
+ * @param timeout the maximum time to wait. If less than
+ * or equal to zero, do not wait at all.
* @throws InterruptedException if interrupted while waiting.
* @see Object#wait(long, int)
*/
public void timedWait(Object obj, long timeout)
- throws InterruptedException {
+ throws InterruptedException {
if (timeout > 0) {
long ms = toMillis(timeout);
int ns = excessNanos(timeout, ms);
@@ -202,16 +261,17 @@ public enum TimeUnit {
}
/**
- * Perform a timed <tt>Thread.join</tt> using this time unit.
+ * Performs a timed <tt>Thread.join</tt> using this time unit.
* This is a convenience method that converts time arguments into the
* form required by the <tt>Thread.join</tt> method.
* @param thread the thread to wait for
- * @param timeout the maximum time to wait
+ * @param timeout the maximum time to wait. If less than
+ * or equal to zero, do not wait at all.
* @throws InterruptedException if interrupted while waiting.
* @see Thread#join(long, int)
*/
public void timedJoin(Thread thread, long timeout)
- throws InterruptedException {
+ throws InterruptedException {
if (timeout > 0) {
long ms = toMillis(timeout);
int ns = excessNanos(timeout, ms);
@@ -220,10 +280,11 @@ public enum TimeUnit {
}
/**
- * Perform a <tt>Thread.sleep</tt> using this unit.
+ * Performs a <tt>Thread.sleep</tt> using this unit.
* This is a convenience method that converts time arguments into the
* form required by the <tt>Thread.sleep</tt> method.
- * @param timeout the minimum time to sleep
+ * @param timeout the minimum time to sleep. If less than
+ * or equal to zero, do not sleep at all.
* @throws InterruptedException if interrupted while sleeping.
* @see Thread#sleep
*/
diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java
index fdd3d49..ba60556 100644
--- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java
+++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java
@@ -8,10 +8,10 @@ package java.util.concurrent.atomic;
import sun.misc.Unsafe;
/**
- * A <tt>boolean</tt> value that may be updated atomically. See the
+ * A {@code boolean} value that may be updated atomically. See the
* {@link java.util.concurrent.atomic} package specification for
* description of the properties of atomic variables. An
- * <tt>AtomicBoolean</tt> is used in applications such as atomically
+ * {@code AtomicBoolean} is used in applications such as atomically
* updated flags, and cannot be used as a replacement for a
* {@link java.lang.Boolean}.
*
@@ -37,7 +37,7 @@ public class AtomicBoolean implements java.io.Serializable {
private volatile int value;
/**
- * Creates a new <tt>AtomicBoolean</tt> with the given initial value.
+ * Creates a new {@code AtomicBoolean} with the given initial value.
*
* @param initialValue the initial value
*/
@@ -46,7 +46,7 @@ public class AtomicBoolean implements java.io.Serializable {
}
/**
- * Creates a new <tt>AtomicBoolean</tt> with initial value <tt>false</tt>.
+ * Creates a new {@code AtomicBoolean} with initial value {@code false}.
*/
public AtomicBoolean() {
}
@@ -61,16 +61,13 @@ public class AtomicBoolean implements java.io.Serializable {
}
/**
- * Atomically sets the value to the given update value if the
- * current value is equal to the expected value. Any given
- * invocation of this operation may fail (return
- * <tt>false</tt>) spuriously, but repeated invocation when
- * the current value holds the expected value and no other thread
- * is also attempting to set the value will eventually succeed.
+ * Atomically sets the value to the given updated value
+ * if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
- * @return true if successful
+ * @return true if successful. False return indicates that
+ * the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(boolean expect, boolean update) {
int e = expect ? 1 : 0;
@@ -79,9 +76,13 @@ public class AtomicBoolean implements java.io.Serializable {
}
/**
- * Atomically set the value to the given updated value
- * if the current value <tt>==</tt> the expected value.
- * May fail spuriously.
+ * Atomically sets the value to the given updated value
+ * if the current value {@code ==} the expected value.
+ *
+ * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
+ * and does not provide ordering guarantees, so is only rarely an
+ * appropriate alternative to {@code compareAndSet}.
+ *
* @param expect the expected value
* @param update the new value
* @return true if successful.
@@ -102,7 +103,7 @@ public class AtomicBoolean implements java.io.Serializable {
}
/**
- * Sets to the given value and returns the previous value.
+ * Atomically sets to the given value and returns the previous value.
*
* @param newValue the new value
* @return the previous value
diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java
index 2b9d15c..1aa32f4 100644
--- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java
+++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java
@@ -8,20 +8,19 @@ package java.util.concurrent.atomic;
import sun.misc.Unsafe;
/**
- * An <tt>int</tt> value that may be updated atomically. See the
+ * An {@code int} value that may be updated atomically. See the
* {@link java.util.concurrent.atomic} package specification for
* description of the properties of atomic variables. An
- * <tt>AtomicInteger</tt> is used in applications such as atomically
+ * {@code AtomicInteger} is used in applications such as atomically
* incremented counters, and cannot be used as a replacement for an
* {@link java.lang.Integer}. However, this class does extend
- * <tt>Number</tt> to allow uniform access by tools and utilities that
+ * {@code Number} to allow uniform access by tools and utilities that
* deal with numerically-based classes.
- *
*
* @since 1.5
* @author Doug Lea
*/
-public class AtomicInteger extends Number implements java.io.Serializable {
+public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
@@ -34,13 +33,13 @@ public class AtomicInteger extends Number implements java.io.Serializable {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
- } catch(Exception ex) { throw new Error(ex); }
+ } catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
/**
- * Create a new AtomicInteger with the given initial value.
+ * Creates a new AtomicInteger with the given initial value.
*
* @param initialValue the initial value
*/
@@ -49,22 +48,22 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
- * Create a new AtomicInteger with initial value <tt>0</tt>.
+ * Creates a new AtomicInteger with initial value {@code 0}.
*/
public AtomicInteger() {
}
/**
- * Get the current value.
+ * Gets the current value.
*
* @return the current value
*/
public final int get() {
return value;
}
-
+
/**
- * Set to the given value.
+ * Sets to the given value.
*
* @param newValue the new value
*/
@@ -73,7 +72,7 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
- * Set to the give value and return the old value.
+ * Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
@@ -85,35 +84,39 @@ public class AtomicInteger extends Number implements java.io.Serializable {
return current;
}
}
-
-
+
/**
- * Atomically set the value to the given updated value
- * if the current value <tt>==</tt> the expected value.
+ * Atomically sets the value to the given updated value
+ * if the current value {@code ==} the expected value.
+ *
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
- return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
+ return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
/**
- * Atomically set the value to the given updated value
- * if the current value <tt>==</tt> the expected value.
- * May fail spuriously.
+ * Atomically sets the value to the given updated value
+ * if the current value {@code ==} the expected value.
+ *
+ * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
+ * and does not provide ordering guarantees, so is only rarely an
+ * appropriate alternative to {@code compareAndSet}.
+ *
* @param expect the expected value
* @param update the new value
* @return true if successful.
*/
public final boolean weakCompareAndSet(int expect, int update) {
- return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
+ return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
-
/**
- * Atomically increment by one the current value.
+ * Atomically increments by one the current value.
+ *
* @return the previous value
*/
public final int getAndIncrement() {
@@ -124,10 +127,10 @@ public class AtomicInteger extends Number implements java.io.Serializable {
return current;
}
}
-
-
+
/**
- * Atomically decrement by one the current value.
+ * Atomically decrements by one the current value.
+ *
* @return the previous value
*/
public final int getAndDecrement() {
@@ -138,10 +141,10 @@ public class AtomicInteger extends Number implements java.io.Serializable {
return current;
}
}
-
-
+
/**
- * Atomically add the given value to current value.
+ * Atomically adds the given value to the current value.
+ *
* @param delta the value to add
* @return the previous value
*/
@@ -155,7 +158,8 @@ public class AtomicInteger extends Number implements java.io.Serializable {
}
/**
- * Atomically increment by one the current value.
+ * Atomically increments by one the current value.
+ *
* @return the updated value
*/
public final int incrementAndGet() {
@@ -166,9 +170,10 @@ public class AtomicInteger extends Number implements java.io.Serializable {
return next;
}
}
-
+
/**
- * Atomically decrement by one the current value.
+ * Atomically decrements by one the current value.
+ *
* @return the updated value
*/
public final int decrementAndGet() {
@@ -179,10 +184,10 @@ public class AtomicInteger extends Number implements java.io.Serializable {
return next;
}
}
-
-
+
/**
- * Atomically add the given value to current value.
+ * Atomically adds the given value to the current value.
+ *
* @param delta the value to add
* @return the updated value
*/
diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java
index 95807f3..ee21b13 100644
--- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java
+++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java
@@ -9,14 +9,14 @@ import sun.misc.Unsafe;
import java.util.*;
/**
- * An <tt>int</tt> array in which elements may be updated atomically.
+ * An {@code int} array in which elements may be updated atomically.
* See the {@link java.util.concurrent.atomic} package
* specification for description of the properties of atomic
* variables.
* @since 1.5
* @author Doug Lea
*/
-public class AtomicIntegerArray implements java.io.Serializable {
+public class AtomicIntegerArray implements java.io.Serializable {
private static final long serialVersionUID = 2862133569453604235L;
// setup to use Unsafe.compareAndSwapInt for updates
@@ -34,7 +34,7 @@ public class AtomicIntegerArray implements java.io.Serializable {
}
/**
- * Create a new AtomicIntegerArray of given length.
+ * Creates a new AtomicIntegerArray of given length.
*
* @param length the length of the array
*/
@@ -46,14 +46,14 @@ public class AtomicIntegerArray implements java.io.Serializable {
}
/**
- * Create a new AtomicIntegerArray with the same length as, and
+ * Creates a new AtomicIntegerArray with the same length as, and
* all elements copied from, the given array.
*
* @param array the array to copy elements from
* @throws NullPointerException if array is null
*/
public AtomicIntegerArray(int[] array) {
- if (array == null)
+ if (array == null)
throw new NullPointerException();
int length = array.length;
this.array = new int[length];
@@ -76,7 +76,7 @@ public class AtomicIntegerArray implements java.io.Serializable {
}
/**
- * Get the current value at position <tt>i</tt>.
+ * Gets the current value at position {@code i}.
*
* @param i the index
* @return the current value
@@ -84,9 +84,9 @@ public class AtomicIntegerArray implements java.io.Serializable {
public final int get(int i) {
return unsafe.getIntVolatile(array, rawIndex(i));
}
-
+
/**
- * Set the element at position <tt>i</tt> to the given value.
+ * Sets the element at position {@code i} to the given value.
*
* @param i the index
* @param newValue the new value
@@ -94,10 +94,10 @@ public class AtomicIntegerArray implements java.io.Serializable {
public final void set(int i, int newValue) {
unsafe.putIntVolatile(array, rawIndex(i), newValue);
}
-
+
/**
- * Set the element at position <tt>i</tt> to the given value and return the
- * old value.
+ * Atomically sets the element at position {@code i} to the given
+ * value and returns the old value.
*
* @param i the index
* @param newValue the new value
@@ -110,10 +110,10 @@ public class AtomicIntegerArray implements java.io.Serializable {
return current;
}
}
-
+
/**
- * Atomically set the value to the given updated value
- * if the current value <tt>==</tt> the expected value.
+ * Atomically sets the element at position {@code i} to the given
+ * updated value if the current value {@code ==} the expected value.
*
* @param i the index
* @param expect the expected value
@@ -122,14 +122,17 @@ public class AtomicIntegerArray implements java.io.Serializable {
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int i, int expect, int update) {
- return unsafe.compareAndSwapInt(array, rawIndex(i),
+ return unsafe.compareAndSwapInt(array, rawIndex(i),
expect, update);
}
/**
- * Atomically set the value to the given updated value
- * if the current value <tt>==</tt> the expected value.
- * May fail spuriously.
+ * Atomically sets the element at position {@code i} to the given
+ * updated value if the current value {@code ==} the expected value.
+ *
+ * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
+ * and does not provide ordering guarantees, so is only rarely an
+ * appropriate alternative to {@code compareAndSet}.
*
* @param i the index
* @param expect the expected value
@@ -141,10 +144,10 @@ public class AtomicIntegerArray implements java.io.Serializable {
}
/**
- * Atomically increment by one the element at index <tt>i</tt>.
+ * Atomically increments by one the element at index {@code i}.
*
* @param i the index
- * @return the previous value;
+ * @return the previous value
*/
public final int getAndIncrement(int i) {
while (true) {
@@ -154,12 +157,12 @@ public class AtomicIntegerArray implements java.io.Serializable {
return current;
}
}
-
+
/**
- * Atomically decrement by one the element at index <tt>i</tt>.
+ * Atomically decrements by one the element at index {@code i}.
*
* @param i the index
- * @return the previous value;
+ * @return the previous value
*/
public final int getAndDecrement(int i) {
while (true) {
@@ -169,13 +172,13 @@ public class AtomicIntegerArray implements java.io.Serializable {
return current;
}
}
-
+
/**
- * Atomically add the given value to element at index <tt>i</tt>.
+ * Atomically adds the given value to the element at index {@code i}.
*
* @param i the index
* @param delta the value to add
- * @return the previous value;
+ * @return the previous value
*/
public final int getAndAdd(int i, int delta) {
while (true) {
@@ -187,10 +190,10 @@ public class AtomicIntegerArray implements java.io.Serializable {
}
/**
- * Atomically increment by one the element at index <tt>i</tt>.
+ * Atomically increments by one the element at index {@code i}.
*
* @param i the index
- * @return the updated value;
+ * @return the updated value
*/
public final int incrementAndGet(int i) {
while (true) {
@@ -200,12 +203,12 @@ public class AtomicIntegerArray implements java.io.Serializable {
return next;
}
}
-
+
/**
- * Atomically decrement by one the element at index <tt>i</tt>.
+ * Atomically decrements by one the element at index {@code i}.
*
* @param i the index
- * @return the updated value;
+ * @return the updated value
*/
public final int decrementAndGet(int i) {
while (true) {
@@ -215,13 +218,13 @@ public class AtomicIntegerArray implements java.io.Serializable {
return next;
}
}
-
+
/**
- * Atomically add the given value to element at index <tt>i</tt>.
+ * Atomically adds the given value to the element at index {@code i}.
*
* @param i the index
* @param delta the value to add
- * @return the updated value;
+ * @return the updated value
*/
public final int addAndGet(int i, int delta) {
while (true) {
@@ -231,7 +234,7 @@ public class AtomicIntegerArray implements java.io.Serializable {
return next;
}
}
-
+
/**
* Returns the String representation of the current values of array.
* @return the String representation of the current values of array.
diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
index 5f289c2..f8d2c81 100644
--- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
+++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
@@ -8,35 +8,40 @@ package java.util.concurrent.atomic;
import sun.misc.Unsafe;
import java.lang.reflect.*;
+import org.apache.harmony.kernel.vm.VM;
+import dalvik.system.VMStack;
+
/**
* A reflection-based utility that enables atomic updates to
- * designated <tt>volatile int</tt> fields of designated classes.
+ * designated {@code volatile int} fields of designated classes.
* This class is designed for use in atomic data structures in which
* several fields of the same node are independently subject to atomic
* updates.
*
- * <p> Note that the guarantees of the <tt>compareAndSet</tt> method
- * in this class are weaker than in other atomic classes. Because this
- * class cannot ensure that all uses of the field are appropriate for
- * purposes of atomic access, it can guarantee atomicity and volatile
- * semantics only with respect to other invocations of
- * <tt>compareAndSet</tt> and <tt>set</tt>.
+ * <p>Note that the guarantees of the {@code compareAndSet}
+ * method in this class are weaker than in other atomic classes.
+ * Because this class cannot ensure that all uses of the field
+ * are appropriate for purposes of atomic access, it can
+ * guarantee atomicity only with respect to other invocations of
+ * {@code compareAndSet} and {@code set} on the same updater.
+ *
* @since 1.5
* @author Doug Lea
* @param <T> The type of the object holding the updatable field
*/
public abstract class AtomicIntegerFieldUpdater<T> {
/**
- * Creates an updater for objects with the given field. The Class
- * argument is needed to check that reflective types and generic
- * types match.
+ * Creates and returns an updater for objects with the given field.
+ * The Class argument is needed to check that reflective types and
+ * generic types match.
+ *
* @param tclass the class of the objects holding the field
- * @param fieldName the name of the field to be updated.
+ * @param fieldName the name of the field to be updated
* @return the updater
* @throws IllegalArgumentException if the field is not a
- * volatile integer type.
+ * volatile integer type
* @throws RuntimeException with a nested reflection-based
- * exception if the class does not hold field or is the wrong type.
+ * exception if the class does not hold field or is the wrong type
*/
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) {
return new AtomicIntegerFieldUpdaterImpl<U>(tclass, fieldName);
@@ -49,57 +54,63 @@ public abstract class AtomicIntegerFieldUpdater<T> {
}
/**
- * Atomically set the value of the field of the given object managed
- * by this Updater to the given updated value if the current value
- * <tt>==</tt> the expected value. This method is guaranteed to be
- * atomic with respect to other calls to <tt>compareAndSet</tt> and
- * <tt>set</tt>, but not necessarily with respect to other
- * changes in the field.
+ * Atomically sets the field of the given object managed by this updater
+ * to the given updated value if the current value {@code ==} the
+ * expected value. This method is guaranteed to be atomic with respect to
+ * other calls to {@code compareAndSet} and {@code set}, but not
+ * necessarily with respect to other changes in the field.
+ *
* @param obj An object whose field to conditionally set
* @param expect the expected value
* @param update the new value
- * @return true if successful.
- * @throws ClassCastException if <tt>obj</tt> is not an instance
- * of the class possessing the field established in the constructor.
+ * @return true if successful
+ * @throws ClassCastException if {@code obj} is not an instance
+ * of the class possessing the field established in the constructor
*/
-
public abstract boolean compareAndSet(T obj, int expect, int update);
/**
- * Atomically set the value of the field of the given object managed
- * by this Updater to the given updated value if the current value
- * <tt>==</tt> the expected value. This method is guaranteed to be
- * atomic with respect to other calls to <tt>compareAndSet</tt> and
- * <tt>set</tt>, but not necessarily with respect to other
- * changes in the field, and may fail spuriously.
+ * Atomically sets the field of the given object managed by this updater
+ * to the given updated value if the current value {@code ==} the
+ * expected value. This method is guaranteed to be atomic with respect to
+ * other calls to {@code compareAndSet} and {@code set}, but not
+ * necessarily with respect to other changes in the field.
+ *
+ * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
+ * and does not provide ordering guarantees, so is only rarely an
+ * appropriate alternative to {@code compareAndSet}.
+ *
* @param obj An object whose field to conditionally set
* @param expect the expected value
* @param update the new value
- * @return true if successful.
- * @throws ClassCastException if <tt>obj</tt> is not an instance
- * of the class possessing the field established in the constructor.
+ * @return true if successful
+ * @throws ClassCastException if {@code obj} is not an instance
+ * of the class possessing the field established in the constructor
*/
-
public abstract boolean weakCompareAndSet(T obj, int expect, int update);
/**
- * Set the field of the given object managed by this updater. This
- * operation is guaranteed to act as a volatile store with respect
- * to subsequent invocations of <tt>compareAndSet</tt>.
+ * Sets the field of the given object managed by this updater to the
+ * given updated value. This operation is guaranteed to act as a volatile
+ * store with respect to subsequent invocations of {@code compareAndSet}.
+ *
* @param obj An object whose field to set
* @param newValue the new value
*/
public abstract void set(T obj, int newValue);
/**
- * Get the current value held in the field by the given object.
+ * Gets the current value held in the field of the given object managed
+ * by this updater.
+ *
* @param obj An object whose field to get
* @return the current value
*/
public abstract int get(T obj);
/**
- * Set to the given value and return the old value.
+ * Atomically sets the field of the given object managed by this updater
+ * to the given value and returns the old value.
*
* @param obj An object whose field to get and set
* @param newValue the new value
@@ -114,9 +125,11 @@ public abstract class AtomicIntegerFieldUpdater<T> {
}
/**
- * Atomically increment by one the current value.
+ * Atomically increments by one the current value of the field of the
+ * given object managed by this updater.
+ *
* @param obj An object whose field to get and set
- * @return the previous value;
+ * @return the previous value
*/
public int getAndIncrement(T obj) {
for (;;) {
@@ -127,11 +140,12 @@ public abstract class AtomicIntegerFieldUpdater<T> {
}
}
-
/**
- * Atomically decrement by one the current value.
+ * Atomically decrements by one the current value of the field of the
+ * given object managed by this updater.
+ *
* @param obj An object whose field to get and set
- * @return the previous value;
+ * @return the previous value
*/
public int getAndDecrement(T obj) {
for (;;) {
@@ -142,12 +156,13 @@ public abstract class AtomicIntegerFieldUpdater<T> {
}
}
-
/**
- * Atomically add the given value to current value.
+ * Atomically adds the given value to the current value of the field of
+ * the given object managed by this updater.
+ *
* @param obj An object whose field to get and set
* @param delta the value to add
- * @return the previous value;
+ * @return the previous value
*/
public int getAndAdd(T obj, int delta) {
for (;;) {
@@ -159,9 +174,11 @@ public abstract class AtomicIntegerFieldUpdater<T> {
}
/**
- * Atomically increment by one the current value.
+ * Atomically increments by one the current value of the field of the
+ * given object managed by this updater.
+ *
* @param obj An object whose field to get and set
- * @return the updated value;
+ * @return the updated value
*/
public int incrementAndGet(T obj) {
for (;;) {
@@ -172,11 +189,12 @@ public abstract class AtomicIntegerFieldUpdater<T> {
}
}
-
/**
- * Atomically decrement by one the current value.
+ * Atomically decrements by one the current value of the field of the
+ * given object managed by this updater.
+ *
* @param obj An object whose field to get and set
- * @return the updated value;
+ * @return the updated value
*/
public int decrementAndGet(T obj) {
for (;;) {
@@ -187,12 +205,13 @@ public abstract class AtomicIntegerFieldUpdater<T> {
}
}
-
/**
- * Atomically add the given value to current value.
+ * Atomically adds the given value to the current value of the field of
+ * the given object managed by this updater.
+ *
* @param obj An object whose field to get and set
* @param delta the value to add
- * @return the updated value;
+ * @return the updated value
*/
public int addAndGet(T obj, int delta) {
for (;;) {
@@ -212,49 +231,83 @@ public abstract class AtomicIntegerFieldUpdater<T> {
// END android-changed
private final long offset;
private final Class<T> tclass;
+ private final Class cclass;
AtomicIntegerFieldUpdaterImpl(Class<T> tclass, String fieldName) {
Field field = null;
+ Class caller = null;
+ int modifiers = 0;
try {
field = tclass.getDeclaredField(fieldName);
- } catch(Exception ex) {
+ // BEGIN android-changed
+ caller = VMStack.getStackClass2();
+ // END android-changed
+ modifiers = field.getModifiers();
+
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ int type = Modifier.isPublic(modifiers)
+ ? Member.PUBLIC : Member.DECLARED;
+ smgr.checkMemberAccess(tclass, type);
+ smgr.checkPackageAccess(tclass.getPackage().getName());
+ }
+ } catch (Exception ex) {
throw new RuntimeException(ex);
}
-
+
Class fieldt = field.getType();
if (fieldt != int.class)
throw new IllegalArgumentException("Must be integer type");
-
- if (!Modifier.isVolatile(field.getModifiers()))
+
+ if (!Modifier.isVolatile(modifiers))
throw new IllegalArgumentException("Must be volatile type");
-
+
+ this.cclass = (Modifier.isProtected(modifiers) &&
+ caller != tclass) ? caller : null;
this.tclass = tclass;
offset = unsafe.objectFieldOffset(field);
}
- public boolean compareAndSet(T obj, int expect, int update) {
+ private void fullCheck(T obj) {
if (!tclass.isInstance(obj))
throw new ClassCastException();
+ if (cclass != null)
+ ensureProtectedAccess(obj);
+ }
+
+ public boolean compareAndSet(T obj, int expect, int update) {
+ if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
return unsafe.compareAndSwapInt(obj, offset, expect, update);
}
public boolean weakCompareAndSet(T obj, int expect, int update) {
- if (!tclass.isInstance(obj))
- throw new ClassCastException();
+ if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
return unsafe.compareAndSwapInt(obj, offset, expect, update);
}
public void set(T obj, int newValue) {
- if (!tclass.isInstance(obj))
- throw new ClassCastException();
+ if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
unsafe.putIntVolatile(obj, offset, newValue);
}
public final int get(T obj) {
- if (!tclass.isInstance(obj))
- throw new ClassCastException();
+ if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
return unsafe.getIntVolatile(obj, offset);
}
+
+ private void ensureProtectedAccess(T obj) {
+ if (cclass.isInstance(obj)) {
+ return;
+ }
+ throw new RuntimeException(
+ new IllegalAccessException("Class " +
+ cclass.getName() +
+ " can not access a protected member of class " +
+ tclass.getName() +
+ " using an instance of " +
+ obj.getClass().getName()
+ )
+ );
+ }
}
}
-
diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java
index 7f41364..9b56002 100644
--- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java
+++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java
@@ -8,19 +8,19 @@ package java.util.concurrent.atomic;
import sun.misc.Unsafe;
/**
- * A <tt>long</tt> value that may be updated atomically. See the
+ * A {@code long} value that may be updated atomically. See the
* {@link java.util.concurrent.atomic} package specification for
* description of the properties of atomic variables. An
- * <tt>AtomicLong</tt> is used in applications such as atomically
+ * {@code AtomicLong} is used in applications such as atomically
* incremented sequence numbers, and cannot be used as a replacement
* for a {@link java.lang.Long}. However, this class does extend
- * <tt>Number</tt> to allow uniform access by tools and utilities that
+ * {@code Number} to allow uniform access by tools and utilities that
* deal with numerically-based classes.
*
* @since 1.5
* @author Doug Lea
*/
-public class AtomicLong extends Number implements java.io.Serializable {
+public class AtomicLong extends Number implements java.io.Serializable {
private static final long serialVersionUID = 1927816293512124184L;
// setup to use Unsafe.compareAndSwapLong for updates
@@ -30,12 +30,14 @@ public class AtomicLong extends Number implements java.io.Serializable {
private static final long valueOffset;
/**
- * Record whether the underlying JVM supports lockless
- * CompareAndSet for longs. While the unsafe.CompareAndSetLong
+ * Records whether the underlying JVM supports lockless
+ * compareAndSwap for longs. While the Unsafe.compareAndSwapLong
* method works in either case, some constructions should be
* handled at Java level to avoid locking user-visible locks.
+ *
+ * Initialised in the static block.
*/
- static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
+ static final boolean VM_SUPPORTS_LONG_CAS;
/**
* Returns whether underlying JVM supports lockless CompareAndSet
@@ -47,13 +49,22 @@ public class AtomicLong extends Number implements java.io.Serializable {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicLong.class.getDeclaredField("value"));
- } catch(Exception ex) { throw new Error(ex); }
+ } catch (Exception ex) { throw new Error(ex); }
+
+ boolean longCASSupport;
+ try {
+ longCASSupport = VMSupportsCS8();
+ } catch (UnsatisfiedLinkError e) {
+ // assume there's support if the native isn't provided by the VM
+ longCASSupport = true;
+ }
+ VM_SUPPORTS_LONG_CAS = longCASSupport;
}
private volatile long value;
/**
- * Create a new AtomicLong with the given initial value.
+ * Creates a new AtomicLong with the given initial value.
*
* @param initialValue the initial value
*/
@@ -62,31 +73,31 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
/**
- * Create a new AtomicLong with initial value <tt>0</tt>.
+ * Creates a new AtomicLong with initial value {@code 0}.
*/
public AtomicLong() {
}
-
+
/**
- * Get the current value.
+ * Gets the current value.
*
* @return the current value
*/
public final long get() {
return value;
}
-
+
/**
- * Set to the given value.
+ * Sets to the given value.
*
* @param newValue the new value
*/
public final void set(long newValue) {
value = newValue;
}
-
+
/**
- * Set to the give value and return the old value.
+ * Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
@@ -98,33 +109,39 @@ public class AtomicLong extends Number implements java.io.Serializable {
return current;
}
}
-
+
/**
- * Atomically set the value to the given updated value
- * if the current value <tt>==</tt> the expected value.
+ * Atomically sets the value to the given updated value
+ * if the current value {@code ==} the expected value.
+ *
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(long expect, long update) {
- return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
+ return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
/**
- * Atomically set the value to the given updated value
- * if the current value <tt>==</tt> the expected value.
- * May fail spuriously.
+ * Atomically sets the value to the given updated value
+ * if the current value {@code ==} the expected value.
+ *
+ * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
+ * and does not provide ordering guarantees, so is only rarely an
+ * appropriate alternative to {@code compareAndSet}.
+ *
* @param expect the expected value
* @param update the new value
* @return true if successful.
*/
public final boolean weakCompareAndSet(long expect, long update) {
- return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
+ return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
-
+
/**
- * Atomically increment by one the current value.
+ * Atomically increments by one the current value.
+ *
* @return the previous value
*/
public final long getAndIncrement() {
@@ -135,10 +152,10 @@ public class AtomicLong extends Number implements java.io.Serializable {
return current;
}
}
-
-
+
/**
- * Atomically decrement by one the current value.
+ * Atomically decrements by one the current value.
+ *
* @return the previous value
*/
public final long getAndDecrement() {
@@ -149,10 +166,10 @@ public class AtomicLong extends Number implements java.io.Serializable {
return current;
}
}
-
-
+
/**
- * Atomically add the given value to current value.
+ * Atomically adds the given value to the current value.
+ *
* @param delta the value to add
* @return the previous value
*/
@@ -164,9 +181,10 @@ public class AtomicLong extends Number implements java.io.Serializable {
return current;
}
}
-
+
/**
- * Atomically increment by one the current value.
+ * Atomically increments by one the current value.
+ *
* @return the updated value
*/
public final long incrementAndGet() {
@@ -177,9 +195,10 @@ public class AtomicLong extends Number implements java.io.Serializable {
return next;
}
}
-
+
/**
- * Atomically decrement by one the current value.
+ * Atomically decrements by one the current value.
+ *
* @return the updated value
*/
public final long decrementAndGet() {
@@ -190,10 +209,10 @@ public class AtomicLong extends Number implements java.io.Serializable {
return next;
}
}
-
-
+
/**
- * Atomically add the given value to current value.
+ * Atomically adds the given value to the current value.
+ *
* @param delta the value to add
* @return the updated value
*/
@@ -205,7 +224,7 @@ public class AtomicLong extends Number implements java.io.Serializable {
return next;
}
}
-
+
/**
* Returns the String representation of the current value.
* @return the String representation of the current value.
@@ -220,7 +239,7 @@ public class AtomicLong extends Number implements java.io.Serializable {
}
public long longValue() {
- return (long)get();
+ return get();
}
public float floatValue() {
@@ -230,5 +249,5 @@ public class AtomicLong extends Number implements java.io.Serializable {
public double doubleValue() {
return (double)get();
}
-
+
}
diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java
index a0e76b4..d96f4c2 100644
--- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java
+++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java
@@ -9,13 +9,13 @@ import sun.misc.Unsafe;
import java.util.*;
/**
- * A <tt>long</tt> array in which elements may be updated atomically.
+ * A {@code long} array in which elements may be updated atomically.
* See the {@link java.util.concurrent.atomic} package specification
* for description of the properties of atomic variables.
* @since 1.5
* @author Doug Lea
*/
-public class AtomicLongArray implements java.io.Serializable {
+public class AtomicLongArray implements java.io.Serializable {
private static final long serialVersionUID = -2308431214976778248L;
// setup to use Unsafe.compareAndSwapInt for updates
@@ -33,25 +33,26 @@ public class AtomicLongArray implements java.io.Serializable {
}
/**
- * Create a new AtomicLongArray of given length.
+ * Creates a new AtomicLongArray of given length.
+ *
* @param length the length of the array
*/
public AtomicLongArray(int length) {
array = new long[length];
// must perform at least one volatile write to conform to JMM
- if (length > 0)
+ if (length > 0)
unsafe.putLongVolatile(array, rawIndex(0), 0);
}
/**
- * Create a new AtomicLongArray with the same length as, and
+ * Creates a new AtomicLongArray with the same length as, and
* all elements copied from, the given array.
*
* @param array the array to copy elements from
* @throws NullPointerException if array is null
*/
public AtomicLongArray(long[] array) {
- if (array == null)
+ if (array == null)
throw new NullPointerException();
int length = array.length;
this.array = new long[length];
@@ -74,7 +75,7 @@ public class AtomicLongArray implements java.io.Serializable {
}
/**
- * Get the current value at position <tt>i</tt>.
+ * Gets the current value at position {@code i}.
*
* @param i the index
* @return the current value
@@ -82,9 +83,9 @@ public class AtomicLongArray implements java.io.Serializable {
public final long get(int i) {
return unsafe.getLongVolatile(array, rawIndex(i));
}
-
+
/**
- * Set the element at position <tt>i</tt> to the given value.
+ * Sets the element at position {@code i} to the given value.
*
* @param i the index
* @param newValue the new value
@@ -92,10 +93,10 @@ public class AtomicLongArray implements java.io.Serializable {
public final void set(int i, long newValue) {
unsafe.putLongVolatile(array, rawIndex(i), newValue);
}
-
+
/**
- * Set the element at position <tt>i</tt> to the given value and return the
- * old value.
+ * Atomically sets the element at position {@code i} to the given value
+ * and returns the old value.
*
* @param i the index
* @param newValue the new value
@@ -108,10 +109,11 @@ public class AtomicLongArray implements java.io.Serializable {
return current;
}
}
-
+
/**
- * Atomically set the value to the given updated value
- * if the current value <tt>==</tt> the expected value.
+ * Atomically sets the value to the given updated value
+ * if the current value {@code ==} the expected value.
+ *
* @param i the index
* @param expect the expected value
* @param update the new value
@@ -119,14 +121,18 @@ public class AtomicLongArray implements java.io.Serializable {
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int i, long expect, long update) {
- return unsafe.compareAndSwapLong(array, rawIndex(i),
+ return unsafe.compareAndSwapLong(array, rawIndex(i),
expect, update);
}
/**
- * Atomically set the value to the given updated value
- * if the current value <tt>==</tt> the expected value.
- * May fail spuriously.
+ * Atomically sets the value to the given updated value
+ * if the current value {@code ==} the expected value.
+ *
+ * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
+ * and does not provide ordering guarantees, so is only rarely an
+ * appropriate alternative to {@code compareAndSet}.
+ *
* @param i the index
* @param expect the expected value
* @param update the new value
@@ -137,10 +143,10 @@ public class AtomicLongArray implements java.io.Serializable {
}
/**
- * Atomically increment by one the element at index <tt>i</tt>.
+ * Atomically increments by one the element at index {@code i}.
*
* @param i the index
- * @return the previous value;
+ * @return the previous value
*/
public final long getAndIncrement(int i) {
while (true) {
@@ -150,12 +156,12 @@ public class AtomicLongArray implements java.io.Serializable {
return current;
}
}
-
+
/**
- * Atomically decrement by one the element at index <tt>i</tt>.
+ * Atomically decrements by one the element at index {@code i}.
*
* @param i the index
- * @return the previous value;
+ * @return the previous value
*/
public final long getAndDecrement(int i) {
while (true) {
@@ -165,13 +171,13 @@ public class AtomicLongArray implements java.io.Serializable {
return current;
}
}
-
+
/**
- * Atomically add the given value to element at index <tt>i</tt>.
+ * Atomically adds the given value to the element at index {@code i}.
*
* @param i the index
* @param delta the value to add
- * @return the previous value;
+ * @return the previous value
*/
public final long getAndAdd(int i, long delta) {
while (true) {
@@ -181,13 +187,12 @@ public class AtomicLongArray implements java.io.Serializable {
return current;
}
}
-
/**
- * Atomically increment the element at index <tt>i</tt>.
+ * Atomically increments by one the element at index {@code i}.
*
* @param i the index
- * @return the updated value;
+ * @return the updated value
*/
public final long incrementAndGet(int i) {
while (true) {
@@ -197,12 +202,12 @@ public class AtomicLongArray implements java.io.Serializable {
return next;
}
}
-
+
/**
- * Atomically decrement the element at index <tt>i</tt>.
+ * Atomically decrements by one the element at index {@code i}.
*
* @param i the index
- * @return the updated value;
+ * @return the updated value
*/
public final long decrementAndGet(int i) {
while (true) {
@@ -212,13 +217,13 @@ public class AtomicLongArray implements java.io.Serializable {
return next;
}
}
-
+
/**
- * Atomically add the given value to element at index <tt>i</tt>.
+ * Atomically adds the given value to the element at index {@code i}.
*
* @param i the index
* @param delta the value to add
- * @return the updated value;
+ * @return the updated value
*/
public long addAndGet(int i, long delta) {
while (true) {
@@ -238,5 +243,5 @@ public class AtomicLongArray implements java.io.Serializable {
get(0);
return Arrays.toString(array);
}
-
+
}
diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
index dcb8b2e..d97c2e4 100644
--- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
+++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
@@ -8,19 +8,22 @@ package java.util.concurrent.atomic;
import sun.misc.Unsafe;
import java.lang.reflect.*;
+import org.apache.harmony.kernel.vm.VM;
+import dalvik.system.VMStack;
+
/**
* A reflection-based utility that enables atomic updates to
- * designated <tt>volatile long</tt> fields of designated classes.
+ * designated {@code volatile long} fields of designated classes.
* This class is designed for use in atomic data structures in which
* several fields of the same node are independently subject to atomic
* updates.
*
- * <p> Note that the guarantees of the <tt>compareAndSet</tt> method
- * in this class are weaker than in other atomic classes. Because this
- * class cannot ensure that all uses of the field are appropriate for
- * purposes of atomic access, it can guarantee atomicity and volatile
- * semantics only with respect to other invocations of
- * <tt>compareAndSet</tt> and <tt>set</tt>.
+ * <p>Note that the guarantees of the {@code compareAndSet}
+ * method in this class are weaker than in other atomic classes.
+ * Because this class cannot ensure that all uses of the field
+ * are appropriate for purposes of atomic access, it can
+ * guarantee atomicity only with respect to other invocations of
+ * {@code compareAndSet} and {@code set} on the same updater.
*
* @since 1.5
* @author Doug Lea
@@ -28,9 +31,10 @@ import java.lang.reflect.*;
*/
public abstract class AtomicLongFieldUpdater<T> {
/**
- * Creates an updater for objects with the given field. The Class
- * argument is needed to check that reflective types and generic
- * types match.
+ * Creates and returns an updater for objects with the given field.
+ * The Class argument is needed to check that reflective types and
+ * generic types match.
+ *
* @param tclass the class of the objects holding the field
* @param fieldName the name of the field to be updated.
* @return the updater
@@ -53,57 +57,63 @@ public abstract class AtomicLongFieldUpdater<T> {
}
/**
- * Atomically set the value of the field of the given object managed
- * by this Updater to the given updated value if the current value
- * <tt>==</tt> the expected value. This method is guaranteed to be
- * atomic with respect to other calls to <tt>compareAndSet</tt> and
- * <tt>set</tt>, but not necessarily with respect to other
- * changes in the field.
+ * Atomically sets the field of the given object managed by this updater
+ * to the given updated value if the current value {@code ==} the
+ * expected value. This method is guaranteed to be atomic with respect to
+ * other calls to {@code compareAndSet} and {@code set}, but not
+ * necessarily with respect to other changes in the field.
+ *
* @param obj An object whose field to conditionally set
* @param expect the expected value
* @param update the new value
* @return true if successful.
- * @throws ClassCastException if <tt>obj</tt> is not an instance
+ * @throws ClassCastException if {@code obj} is not an instance
* of the class possessing the field established in the constructor.
*/
-
public abstract boolean compareAndSet(T obj, long expect, long update);
/**
- * Atomically set the value of the field of the given object managed
- * by this Updater to the given updated value if the current value
- * <tt>==</tt> the expected value. This method is guaranteed to be
- * atomic with respect to other calls to <tt>compareAndSet</tt> and
- * <tt>set</tt>, but not necessarily with respect to other
- * changes in the field, and may fail spuriously.
+ * Atomically sets the field of the given object managed by this updater
+ * to the given updated value if the current value {@code ==} the
+ * expected value. This method is guaranteed to be atomic with respect to
+ * other calls to {@code compareAndSet} and {@code set}, but not
+ * necessarily with respect to other changes in the field.
+ *
+ * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
+ * and does not provide ordering guarantees, so is only rarely an
+ * appropriate alternative to {@code compareAndSet}.
+ *
* @param obj An object whose field to conditionally set
* @param expect the expected value
* @param update the new value
* @return true if successful.
- * @throws ClassCastException if <tt>obj</tt> is not an instance
+ * @throws ClassCastException if {@code obj} is not an instance
* of the class possessing the field established in the constructor.
*/
-
public abstract boolean weakCompareAndSet(T obj, long expect, long update);
/**
- * Set the field of the given object managed by this updater. This
- * operation is guaranteed to act as a volatile store with respect
- * to subsequent invocations of <tt>compareAndSet</tt>.
+ * Sets the field of the given object managed by this updater to the
+ * given updated value. This operation is guaranteed to act as a volatile
+ * store with respect to subsequent invocations of {@code compareAndSet}.
+ *
* @param obj An object whose field to set
* @param newValue the new value
*/
public abstract void set(T obj, long newValue);
/**
- * Get the current value held in the field by the given object.
+ * Gets the current value held in the field of the given object managed
+ * by this updater.
+ *
* @param obj An object whose field to get
* @return the current value
*/
public abstract long get(T obj);
/**
- * Set to the given value and return the old value.
+ * Atomically sets the field of the given object managed by this updater
+ * to the given value and returns the old value.
*
* @param obj An object whose field to get and set
* @param newValue the new value
@@ -118,9 +128,11 @@ public abstract class AtomicLongFieldUpdater<T> {
}
/**
- * Atomically increment by one the current value.
+ * Atomically increments by one the current value of the field of the
+ * given object managed by this updater.
+ *
* @param obj An object whose field to get and set
- * @return the previous value;
+ * @return the previous value
*/
public long getAndIncrement(T obj) {
for (;;) {
@@ -131,11 +143,12 @@ public abstract class AtomicLongFieldUpdater<T> {
}
}
-
/**
- * Atomically decrement by one the current value.
+ * Atomically decrements by one the current value of the field of the
+ * given object managed by this updater.
+ *
* @param obj An object whose field to get and set
- * @return the previous value;
+ * @return the previous value
*/
public long getAndDecrement(T obj) {
for (;;) {
@@ -146,12 +159,13 @@ public abstract class AtomicLongFieldUpdater<T> {
}
}
-
/**
- * Atomically add the given value to current value.
+ * Atomically adds the given value to the current value of the field of
+ * the given object managed by this updater.
+ *
* @param obj An object whose field to get and set
* @param delta the value to add
- * @return the previous value;
+ * @return the previous value
*/
public long getAndAdd(T obj, long delta) {
for (;;) {
@@ -163,9 +177,11 @@ public abstract class AtomicLongFieldUpdater<T> {
}
/**
- * Atomically increment by one the current value.
+ * Atomically increments by one the current value of the field of the
+ * given object managed by this updater.
+ *
* @param obj An object whose field to get and set
- * @return the updated value;
+ * @return the updated value
*/
public long incrementAndGet(T obj) {
for (;;) {
@@ -176,11 +192,12 @@ public abstract class AtomicLongFieldUpdater<T> {
}
}
-
/**
- * Atomically decrement by one the current value.
+ * Atomically decrements by one the current value of the field of the
+ * given object managed by this updater.
+ *
* @param obj An object whose field to get and set
- * @return the updated value;
+ * @return the updated value
*/
public long decrementAndGet(T obj) {
for (;;) {
@@ -191,12 +208,13 @@ public abstract class AtomicLongFieldUpdater<T> {
}
}
-
/**
- * Atomically add the given value to current value.
+ * Atomically adds the given value to the current value of the field of
+ * the given object managed by this updater.
+ *
* @param obj An object whose field to get and set
* @param delta the value to add
- * @return the updated value;
+ * @return the updated value
*/
public long addAndGet(T obj, long delta) {
for (;;) {
@@ -213,49 +231,83 @@ public abstract class AtomicLongFieldUpdater<T> {
// END android-changed
private final long offset;
private final Class<T> tclass;
+ private final Class cclass;
CASUpdater(Class<T> tclass, String fieldName) {
Field field = null;
+ Class caller = null;
+ int modifiers = 0;
try {
field = tclass.getDeclaredField(fieldName);
- } catch(Exception ex) {
+ // BEGIN android-changed
+ caller = VMStack.getStackClass2();
+ // END android-changed
+ modifiers = field.getModifiers();
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ int type = Modifier.isPublic(modifiers)
+ ? Member.PUBLIC : Member.DECLARED;
+ smgr.checkMemberAccess(tclass, type);
+ smgr.checkPackageAccess(tclass.getPackage().getName());
+ }
+ } catch (Exception ex) {
throw new RuntimeException(ex);
}
-
+
Class fieldt = field.getType();
if (fieldt != long.class)
throw new IllegalArgumentException("Must be long type");
-
- if (!Modifier.isVolatile(field.getModifiers()))
+
+ if (!Modifier.isVolatile(modifiers))
throw new IllegalArgumentException("Must be volatile type");
-
+
+ this.cclass = (Modifier.isProtected(modifiers) &&
+ caller != tclass) ? caller : null;
this.tclass = tclass;
offset = unsafe.objectFieldOffset(field);
}
- public boolean compareAndSet(T obj, long expect, long update) {
+ private void fullCheck(T obj) {
if (!tclass.isInstance(obj))
throw new ClassCastException();
+ if (cclass != null)
+ ensureProtectedAccess(obj);
+ }
+
+ public boolean compareAndSet(T obj, long expect, long update) {
+ if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
return unsafe.compareAndSwapLong(obj, offset, expect, update);
}
public boolean weakCompareAndSet(T obj, long expect, long update) {
- if (!tclass.isInstance(obj))
- throw new ClassCastException();
+ if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
return unsafe.compareAndSwapLong(obj, offset, expect, update);
}
public void set(T obj, long newValue) {
- if (!tclass.isInstance(obj))
- throw new ClassCastException();
+ if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
unsafe.putLongVolatile(obj, offset, newValue);
}
public long get(T obj) {
- if (!tclass.isInstance(obj))
- throw new ClassCastException();
+ if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
return unsafe.getLongVolatile(obj, offset);
}
+
+ private void ensureProtectedAccess(T obj) {
+ if (cclass.isInstance(obj)) {
+ return;
+ }
+ throw new RuntimeException (
+ new IllegalAccessException("Class " +
+ cclass.getName() +
+ " can not access a protected member of class " +
+ tclass.getName() +
+ " using an instance of " +
+ obj.getClass().getName()
+ )
+ );
+ }
}
@@ -265,32 +317,54 @@ public abstract class AtomicLongFieldUpdater<T> {
// END android-changed
private final long offset;
private final Class<T> tclass;
+ private final Class cclass;
LockedUpdater(Class<T> tclass, String fieldName) {
Field field = null;
+ Class caller = null;
+ int modifiers = 0;
try {
field = tclass.getDeclaredField(fieldName);
- } catch(Exception ex) {
+ // BEGIN android-changed
+ caller = VMStack.getStackClass2();
+ // END android-changed
+ modifiers = field.getModifiers();
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ int type = Modifier.isPublic(modifiers)
+ ? Member.PUBLIC : Member.DECLARED;
+ smgr.checkMemberAccess(tclass, type);
+ smgr.checkPackageAccess(tclass.getPackage().getName());
+ }
+ } catch (Exception ex) {
throw new RuntimeException(ex);
}
-
+
Class fieldt = field.getType();
if (fieldt != long.class)
throw new IllegalArgumentException("Must be long type");
-
- if (!Modifier.isVolatile(field.getModifiers()))
+
+ if (!Modifier.isVolatile(modifiers))
throw new IllegalArgumentException("Must be volatile type");
-
+
+ this.cclass = (Modifier.isProtected(modifiers) &&
+ caller != tclass) ? caller : null;
this.tclass = tclass;
offset = unsafe.objectFieldOffset(field);
}
- public boolean compareAndSet(T obj, long expect, long update) {
+ private void fullCheck(T obj) {
if (!tclass.isInstance(obj))
throw new ClassCastException();
+ if (cclass != null)
+ ensureProtectedAccess(obj);
+ }
+
+ public boolean compareAndSet(T obj, long expect, long update) {
+ if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
synchronized(this) {
long v = unsafe.getLong(obj, offset);
- if (v != expect)
+ if (v != expect)
return false;
unsafe.putLong(obj, offset, update);
return true;
@@ -302,20 +376,36 @@ public abstract class AtomicLongFieldUpdater<T> {
}
public void set(T obj, long newValue) {
- if (!tclass.isInstance(obj))
- throw new ClassCastException();
+ if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
synchronized(this) {
unsafe.putLong(obj, offset, newValue);
}
}
+ public void lazySet(T obj, long newValue) {
+ set(obj, newValue);
+ }
+
public long get(T obj) {
- if (!tclass.isInstance(obj))
- throw new ClassCastException();
+ if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
synchronized(this) {
return unsafe.getLong(obj, offset);
}
}
+
+ private void ensureProtectedAccess(T obj) {
+ if (cclass.isInstance(obj)) {
+ return;
+ }
+ throw new RuntimeException (
+ new IllegalAccessException("Class " +
+ cclass.getName() +
+ " can not access a protected member of class " +
+ tclass.getName() +
+ " using an instance of " +
+ obj.getClass().getName()
+ )
+ );
+ }
}
}
-
diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java
index 11c91ba..4877dc4 100644
--- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java
+++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java
@@ -7,7 +7,7 @@
package java.util.concurrent.atomic;
/**
- * An <tt>AtomicMarkableReference</tt> maintains an object reference
+ * An {@code AtomicMarkableReference} maintains an object reference
* along with a mark bit, that can be updated atomically.
* <p>
* <p> Implementation note. This implementation maintains markable
@@ -31,7 +31,7 @@ public class AtomicMarkableReference<V> {
private final AtomicReference<ReferenceBooleanPair<V>> atomicRef;
/**
- * Creates a new <tt>AtomicMarkableReference</tt> with the given
+ * Creates a new {@code AtomicMarkableReference} with the given
* initial values.
*
* @param initialRef the initial reference
@@ -61,10 +61,10 @@ public class AtomicMarkableReference<V> {
/**
* Returns the current values of both the reference and the mark.
- * Typical usage is <tt>boolean[1] holder; ref = v.get(holder); </tt>.
+ * Typical usage is {@code boolean[1] holder; ref = v.get(holder); }.
*
* @param markHolder an array of size of at least one. On return,
- * <tt>markholder[0]</tt> will hold the value of the mark.
+ * {@code markholder[0]} will hold the value of the mark.
* @return the current value of the reference
*/
public V get(boolean[] markHolder) {
@@ -76,12 +76,12 @@ public class AtomicMarkableReference<V> {
/**
* Atomically sets the value of both the reference and mark
* to the given update values if the
- * current reference is <tt>==</tt> to the expected reference
- * and the current mark is equal to the expected mark. Any given
- * invocation of this operation may fail (return
- * <tt>false</tt>) spuriously, but repeated invocation when
- * the current value holds the expected value and no other thread
- * is also attempting to set the value will eventually succeed.
+ * current reference is {@code ==} to the expected reference
+ * and the current mark is equal to the expected mark.
+ *
+ * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
+ * and does not provide ordering guarantees, so is only rarely an
+ * appropriate alternative to {@code compareAndSet}.
*
* @param expectedReference the expected value of the reference
* @param newReference the new value for the reference
@@ -93,7 +93,7 @@ public class AtomicMarkableReference<V> {
V newReference,
boolean expectedMark,
boolean newMark) {
- ReferenceBooleanPair current = atomicRef.get();
+ ReferenceBooleanPair<V> current = atomicRef.get();
return expectedReference == current.reference &&
expectedMark == current.bit &&
((newReference == current.reference && newMark == current.bit) ||
@@ -105,8 +105,8 @@ public class AtomicMarkableReference<V> {
/**
* Atomically sets the value of both the reference and mark
* to the given update values if the
- * current reference is <tt>==</tt> to the expected reference
- * and the current mark is equal to the expected mark.
+ * current reference is {@code ==} to the expected reference
+ * and the current mark is equal to the expected mark.
*
* @param expectedReference the expected value of the reference
* @param newReference the new value for the reference
@@ -118,7 +118,7 @@ public class AtomicMarkableReference<V> {
V newReference,
boolean expectedMark,
boolean newMark) {
- ReferenceBooleanPair current = atomicRef.get();
+ ReferenceBooleanPair<V> current = atomicRef.get();
return expectedReference == current.reference &&
expectedMark == current.bit &&
((newReference == current.reference && newMark == current.bit) ||
@@ -134,16 +134,16 @@ public class AtomicMarkableReference<V> {
* @param newMark the new value for the mark
*/
public void set(V newReference, boolean newMark) {
- ReferenceBooleanPair current = atomicRef.get();
+ ReferenceBooleanPair<V> current = atomicRef.get();
if (newReference != current.reference || newMark != current.bit)
atomicRef.set(new ReferenceBooleanPair<V>(newReference, newMark));
}
/**
* Atomically sets the value of the mark to the given update value
- * if the current reference is <tt>==</tt> to the expected
+ * if the current reference is {@code ==} to the expected
* reference. Any given invocation of this operation may fail
- * (return <tt>false</tt>) spuriously, but repeated invocation
+ * (return {@code false}) spuriously, but repeated invocation
* when the current value holds the expected value and no other
* thread is also attempting to set the value will eventually
* succeed.
@@ -153,7 +153,7 @@ public class AtomicMarkableReference<V> {
* @return true if successful
*/
public boolean attemptMark(V expectedReference, boolean newMark) {
- ReferenceBooleanPair current = atomicRef.get();
+ ReferenceBooleanPair<V> current = atomicRef.get();
return expectedReference == current.reference &&
(newMark == current.bit ||
atomicRef.compareAndSet
diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java
index 89c050c..1de9b1c 100644
--- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java
+++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java
@@ -15,7 +15,7 @@ import sun.misc.Unsafe;
* @author Doug Lea
* @param <V> The type of object referred to by this reference
*/
-public class AtomicReference<V> implements java.io.Serializable {
+public class AtomicReference<V> implements java.io.Serializable {
private static final long serialVersionUID = -1848883965231344442L;
// BEGIN android-changed
@@ -27,13 +27,13 @@ public class AtomicReference<V> implements java.io.Serializable {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicReference.class.getDeclaredField("value"));
- } catch(Exception ex) { throw new Error(ex); }
+ } catch (Exception ex) { throw new Error(ex); }
}
private volatile V value;
/**
- * Create a new AtomicReference with the given initial value.
+ * Creates a new AtomicReference with the given initial value.
*
* @param initialValue the initial value
*/
@@ -42,55 +42,59 @@ public class AtomicReference<V> implements java.io.Serializable {
}
/**
- * Create a new AtomicReference with null initial value.
+ * Creates a new AtomicReference with null initial value.
*/
public AtomicReference() {
}
-
+
/**
- * Get the current value.
+ * Gets the current value.
*
* @return the current value
*/
public final V get() {
return value;
}
-
+
/**
- * Set to the given value.
+ * Sets to the given value.
*
* @param newValue the new value
*/
public final void set(V newValue) {
value = newValue;
}
-
+
/**
- * Atomically set the value to the given updated value
- * if the current value <tt>==</tt> the expected value.
+ * Atomically sets the value to the given updated value
+ * if the current value {@code ==} the expected value.
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(V expect, V update) {
- return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
+ return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}
/**
- * Atomically set the value to the given updated value
- * if the current value <tt>==</tt> the expected value.
- * May fail spuriously.
+ * Atomically sets the value to the given updated value
+ * if the current value {@code ==} the expected value.
+ *
+ * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
+ * and does not provide ordering guarantees, so is only rarely an
+ * appropriate alternative to {@code compareAndSet}.
+ *
* @param expect the expected value
* @param update the new value
* @return true if successful.
*/
public final boolean weakCompareAndSet(V expect, V update) {
- return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
+ return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}
/**
- * Set to the given value and return the old value.
+ * Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java
index 9ad800c..9a484e6 100644
--- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java
+++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java
@@ -17,7 +17,7 @@ import java.util.*;
* @author Doug Lea
* @param <E> The base class of elements held in this array
*/
-public class AtomicReferenceArray<E> implements java.io.Serializable {
+public class AtomicReferenceArray<E> implements java.io.Serializable {
private static final long serialVersionUID = -6209656149925076980L;
// BEGIN android-changed
@@ -34,25 +34,25 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
}
/**
- * Create a new AtomicReferenceArray of given length.
+ * Creates a new AtomicReferenceArray of given length.
* @param length the length of the array
*/
public AtomicReferenceArray(int length) {
array = new Object[length];
// must perform at least one volatile write to conform to JMM
- if (length > 0)
+ if (length > 0)
unsafe.putObjectVolatile(array, rawIndex(0), null);
}
/**
- * Create a new AtomicReferenceArray with the same length as, and
+ * Creates a new AtomicReferenceArray with the same length as, and
* all elements copied from, the given array.
*
* @param array the array to copy elements from
* @throws NullPointerException if array is null
*/
public AtomicReferenceArray(E[] array) {
- if (array == null)
+ if (array == null)
throw new NullPointerException();
int length = array.length;
this.array = new Object[length];
@@ -76,7 +76,7 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
}
/**
- * Get the current value at position <tt>i</tt>.
+ * Gets the current value at position {@code i}.
*
* @param i the index
* @return the current value
@@ -84,9 +84,9 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
public final E get(int i) {
return (E) unsafe.getObjectVolatile(array, rawIndex(i));
}
-
+
/**
- * Set the element at position <tt>i</tt> to the given value.
+ * Sets the element at position {@code i} to the given value.
*
* @param i the index
* @param newValue the new value
@@ -94,10 +94,10 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
public final void set(int i, E newValue) {
unsafe.putObjectVolatile(array, rawIndex(i), newValue);
}
-
+
/**
- * Set the element at position <tt>i</tt> to the given value and return the
- * old value.
+ * Atomically sets the element at position {@code i} to the given
+ * value and returns the old value.
*
* @param i the index
* @param newValue the new value
@@ -110,10 +110,10 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
return current;
}
}
-
+
/**
- * Atomically set the value to the given updated value
- * if the current value <tt>==</tt> the expected value.
+ * Atomically sets the element at position {@code i} to the given
+ * updated value if the current value {@code ==} the expected value.
* @param i the index
* @param expect the expected value
* @param update the new value
@@ -121,14 +121,18 @@ public class AtomicReferenceArray<E> implements java.io.Serializable {
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int i, E expect, E update) {
- return unsafe.compareAndSwapObject(array, rawIndex(i),
+ return unsafe.compareAndSwapObject(array, rawIndex(i),
expect, update);
}
/**
- * Atomically set the value to the given updated value
- * if the current value <tt>==</tt> the expected value.
- * May fail spuriously.
+ * Atomically sets the element at position {@code i} to the given
+ * updated value if the current value {@code ==} the expected value.
+ *
+ * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
+ * and does not provide ordering guarantees, so is only rarely an
+ * appropriate alternative to {@code compareAndSet}.
+ *
* @param i the index
* @param expect the expected value
* @param update the new value
diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
index f20661d..fae134f 100644
--- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
+++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
@@ -8,9 +8,12 @@ package java.util.concurrent.atomic;
import sun.misc.Unsafe;
import java.lang.reflect.*;
+import org.apache.harmony.kernel.vm.VM;
+import dalvik.system.VMStack;
+
/**
* A reflection-based utility that enables atomic updates to
- * designated <tt>volatile</tt> reference fields of designated
+ * designated {@code volatile} reference fields of designated
* classes. This class is designed for use in atomic data structures
* in which several reference fields of the same node are
* independently subject to atomic updates. For example, a tree node
@@ -33,12 +36,13 @@ import java.lang.reflect.*;
* }
* </pre>
*
- * <p> Note that the guarantees of the <tt>compareAndSet</tt>
- * method in this class are weaker than in other atomic classes. Because this
- * class cannot ensure that all uses of the field are appropriate for
- * purposes of atomic access, it can guarantee atomicity and volatile
- * semantics only with respect to other invocations of
- * <tt>compareAndSet</tt> and <tt>set</tt>.
+ * <p>Note that the guarantees of the {@code compareAndSet}
+ * method in this class are weaker than in other atomic classes.
+ * Because this class cannot ensure that all uses of the field
+ * are appropriate for purposes of atomic access, it can
+ * guarantee atomicity only with respect to other invocations of
+ * {@code compareAndSet} and {@code set} on the same updater.
+ *
* @since 1.5
* @author Doug Lea
* @param <T> The type of the object holding the updatable field
@@ -47,9 +51,10 @@ import java.lang.reflect.*;
public abstract class AtomicReferenceFieldUpdater<T, V> {
/**
- * Creates an updater for objects with the given field. The Class
- * arguments are needed to check that reflective types and generic
- * types match.
+ * Creates and returns an updater for objects with the given field.
+ * The Class arguments are needed to check that reflective types and
+ * generic types match.
+ *
* @param tclass the class of the objects holding the field.
* @param vclass the class of the field
* @param fieldName the name of the field to be updated.
@@ -59,9 +64,8 @@ public abstract class AtomicReferenceFieldUpdater<T, V> {
* exception if the class does not hold field or is the wrong type.
*/
public static <U, W> AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U> tclass, Class<W> vclass, String fieldName) {
- // Currently rely on standard intrinsics implementation
- return new AtomicReferenceFieldUpdaterImpl<U,W>(tclass,
- vclass,
+ return new AtomicReferenceFieldUpdaterImpl<U,W>(tclass,
+ vclass,
fieldName);
}
@@ -72,27 +76,30 @@ public abstract class AtomicReferenceFieldUpdater<T, V> {
}
/**
- * Atomically set the value of the field of the given object managed
- * by this Updater to the given updated value if the current value
- * <tt>==</tt> the expected value. This method is guaranteed to be
- * atomic with respect to other calls to <tt>compareAndSet</tt> and
- * <tt>set</tt>, but not necessarily with respect to other
- * changes in the field.
+ * Atomically sets the field of the given object managed by this updater
+ * to the given updated value if the current value {@code ==} the
+ * expected value. This method is guaranteed to be atomic with respect to
+ * other calls to {@code compareAndSet} and {@code set}, but not
+ * necessarily with respect to other changes in the field.
+ *
* @param obj An object whose field to conditionally set
* @param expect the expected value
* @param update the new value
* @return true if successful.
*/
-
public abstract boolean compareAndSet(T obj, V expect, V update);
/**
- * Atomically set the value of the field of the given object managed
- * by this Updater to the given updated value if the current value
- * <tt>==</tt> the expected value. This method is guaranteed to be
- * atomic with respect to other calls to <tt>compareAndSet</tt> and
- * <tt>set</tt>, but not necessarily with respect to other
- * changes in the field, and may fail spuriously.
+ * Atomically sets the field of the given object managed by this updater
+ * to the given updated value if the current value {@code ==} the
+ * expected value. This method is guaranteed to be atomic with respect to
+ * other calls to {@code compareAndSet} and {@code set}, but not
+ * necessarily with respect to other changes in the field.
+ *
+ * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
+ * and does not provide ordering guarantees, so is only rarely an
+ * appropriate alternative to {@code compareAndSet}.
+ *
* @param obj An object whose field to conditionally set
* @param expect the expected value
* @param update the new value
@@ -101,23 +108,27 @@ public abstract class AtomicReferenceFieldUpdater<T, V> {
public abstract boolean weakCompareAndSet(T obj, V expect, V update);
/**
- * Set the field of the given object managed by this updater. This
- * operation is guaranteed to act as a volatile store with respect
- * to subsequent invocations of <tt>compareAndSet</tt>.
+ * Sets the field of the given object managed by this updater to the
+ * given updated value. This operation is guaranteed to act as a volatile
+ * store with respect to subsequent invocations of {@code compareAndSet}.
+ *
* @param obj An object whose field to set
* @param newValue the new value
*/
public abstract void set(T obj, V newValue);
/**
- * Get the current value held in the field by the given object.
+ * Gets the current value held in the field of the given object managed
+ * by this updater.
+ *
* @param obj An object whose field to get
* @return the current value
*/
public abstract V get(T obj);
/**
- * Set to the given value and return the old value.
+ * Atomically sets the field of the given object managed by this updater
+ * to the given value and returns the old value.
*
* @param obj An object whose field to get and set
* @param newValue the new value
@@ -131,67 +142,128 @@ public abstract class AtomicReferenceFieldUpdater<T, V> {
}
}
- /**
- * Standard hotspot implementation using intrinsics
- */
- private static class AtomicReferenceFieldUpdaterImpl<T,V> extends AtomicReferenceFieldUpdater<T,V> {
+ private static final class AtomicReferenceFieldUpdaterImpl<T,V>
+ extends AtomicReferenceFieldUpdater<T,V> {
// BEGIN android-changed
private static final Unsafe unsafe = UnsafeAccess.THE_ONE;
// END android-changed
private final long offset;
private final Class<T> tclass;
private final Class<V> vclass;
+ private final Class cclass;
- AtomicReferenceFieldUpdaterImpl(Class<T> tclass, Class<V> vclass, String fieldName) {
+ /*
+ * Internal type checks within all update methods contain
+ * internal inlined optimizations checking for the common
+ * cases where the class is final (in which case a simple
+ * getClass comparison suffices) or is of type Object (in
+ * which case no check is needed because all objects are
+ * instances of Object). The Object case is handled simply by
+ * setting vclass to null in constructor. The targetCheck and
+ * updateCheck methods are invoked when these faster
+ * screenings fail.
+ */
+
+ AtomicReferenceFieldUpdaterImpl(Class<T> tclass,
+ Class<V> vclass,
+ String fieldName) {
Field field = null;
Class fieldClass = null;
+ Class caller = null;
+ int modifiers = 0;
try {
field = tclass.getDeclaredField(fieldName);
+ // BEGIN android-changed
+ caller = VMStack.getStackClass2();
+ // END android-changed
+ modifiers = field.getModifiers();
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) {
+ int type = Modifier.isPublic(modifiers)
+ ? Member.PUBLIC : Member.DECLARED;
+ smgr.checkMemberAccess(tclass, type);
+ smgr.checkPackageAccess(tclass.getPackage().getName());
+ }
fieldClass = field.getType();
- } catch(Exception ex) {
+ } catch (Exception ex) {
throw new RuntimeException(ex);
}
-
+
if (vclass != fieldClass)
throw new ClassCastException();
-
- if (!Modifier.isVolatile(field.getModifiers()))
+
+ if (!Modifier.isVolatile(modifiers))
throw new IllegalArgumentException("Must be volatile type");
+ this.cclass = (Modifier.isProtected(modifiers) &&
+ caller != tclass) ? caller : null;
this.tclass = tclass;
- this.vclass = vclass;
+ if (vclass == Object.class)
+ this.vclass = null;
+ else
+ this.vclass = vclass;
offset = unsafe.objectFieldOffset(field);
}
-
- public boolean compareAndSet(T obj, V expect, V update) {
+ void targetCheck(T obj) {
+ if (!tclass.isInstance(obj))
+ throw new ClassCastException();
+ if (cclass != null)
+ ensureProtectedAccess(obj);
+ }
+
+ void updateCheck(T obj, V update) {
if (!tclass.isInstance(obj) ||
- (update != null && !vclass.isInstance(update)))
+ (update != null && vclass != null && !vclass.isInstance(update)))
throw new ClassCastException();
+ if (cclass != null)
+ ensureProtectedAccess(obj);
+ }
+
+ public boolean compareAndSet(T obj, V expect, V update) {
+ if (obj == null || obj.getClass() != tclass || cclass != null ||
+ (update != null && vclass != null &&
+ vclass != update.getClass()))
+ updateCheck(obj, update);
return unsafe.compareAndSwapObject(obj, offset, expect, update);
}
public boolean weakCompareAndSet(T obj, V expect, V update) {
// same implementation as strong form for now
- if (!tclass.isInstance(obj) ||
- (update != null && !vclass.isInstance(update)))
- throw new ClassCastException();
+ if (obj == null || obj.getClass() != tclass || cclass != null ||
+ (update != null && vclass != null &&
+ vclass != update.getClass()))
+ updateCheck(obj, update);
return unsafe.compareAndSwapObject(obj, offset, expect, update);
}
-
public void set(T obj, V newValue) {
- if (!tclass.isInstance(obj) ||
- (newValue != null && !vclass.isInstance(newValue)))
- throw new ClassCastException();
+ if (obj == null || obj.getClass() != tclass || cclass != null ||
+ (newValue != null && vclass != null &&
+ vclass != newValue.getClass()))
+ updateCheck(obj, newValue);
unsafe.putObjectVolatile(obj, offset, newValue);
}
public V get(T obj) {
- if (!tclass.isInstance(obj))
- throw new ClassCastException();
+ if (obj == null || obj.getClass() != tclass || cclass != null)
+ targetCheck(obj);
return (V)unsafe.getObjectVolatile(obj, offset);
}
+
+ private void ensureProtectedAccess(T obj) {
+ if (cclass.isInstance(obj)) {
+ return;
+ }
+ throw new RuntimeException (
+ new IllegalAccessException("Class " +
+ cclass.getName() +
+ " can not access a protected member of class " +
+ tclass.getName() +
+ " using an instance of " +
+ obj.getClass().getName()
+ )
+ );
+ }
}
}
-
diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java
index b0a02c2..a1d535f 100644
--- a/concurrent/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java
+++ b/concurrent/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java
@@ -7,8 +7,8 @@
package java.util.concurrent.atomic;
/**
- * An <tt>AtomicStampedReference</tt> maintains an object reference
- * along with an integer "stamp", that can be updated atomically.
+ * An {@code AtomicStampedReference} maintains an object reference
+ * along with an integer "stamp", that can be updated atomically.
*
* <p> Implementation note. This implementation maintains stamped
* references by creating internal objects representing "boxed"
@@ -31,7 +31,7 @@ public class AtomicStampedReference<V> {
private final AtomicReference<ReferenceIntegerPair<V>> atomicRef;
/**
- * Creates a new <tt>AtomicStampedReference</tt> with the given
+ * Creates a new {@code AtomicStampedReference} with the given
* initial values.
*
* @param initialRef the initial reference
@@ -62,10 +62,10 @@ public class AtomicStampedReference<V> {
/**
* Returns the current values of both the reference and the stamp.
- * Typical usage is <tt>int[1] holder; ref = v.get(holder); </tt>.
+ * Typical usage is {@code int[1] holder; ref = v.get(holder); }.
*
* @param stampHolder an array of size of at least one. On return,
- * <tt>stampholder[0]</tt> will hold the value of the stamp.
+ * {@code stampholder[0]} will hold the value of the stamp.
* @return the current value of the reference
*/
public V get(int[] stampHolder) {
@@ -77,12 +77,12 @@ public class AtomicStampedReference<V> {
/**
* Atomically sets the value of both the reference and stamp
* to the given update values if the
- * current reference is <tt>==</tt> to the expected reference
- * and the current stamp is equal to the expected stamp. Any given
- * invocation of this operation may fail (return
- * <tt>false</tt>) spuriously, but repeated invocation when
- * the current value holds the expected value and no other thread
- * is also attempting to set the value will eventually succeed.
+ * current reference is {@code ==} to the expected reference
+ * and the current stamp is equal to the expected stamp.
+ *
+ * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
+ * and does not provide ordering guarantees, so is only rarely an
+ * appropriate alternative to {@code compareAndSet}.
*
* @param expectedReference the expected value of the reference
* @param newReference the new value for the reference
@@ -94,7 +94,7 @@ public class AtomicStampedReference<V> {
V newReference,
int expectedStamp,
int newStamp) {
- ReferenceIntegerPair current = atomicRef.get();
+ ReferenceIntegerPair<V> current = atomicRef.get();
return expectedReference == current.reference &&
expectedStamp == current.integer &&
((newReference == current.reference &&
@@ -107,8 +107,8 @@ public class AtomicStampedReference<V> {
/**
* Atomically sets the value of both the reference and stamp
* to the given update values if the
- * current reference is <tt>==</tt> to the expected reference
- * and the current stamp is equal to the expected stamp.
+ * current reference is {@code ==} to the expected reference
+ * and the current stamp is equal to the expected stamp.
*
* @param expectedReference the expected value of the reference
* @param newReference the new value for the reference
@@ -120,7 +120,7 @@ public class AtomicStampedReference<V> {
V newReference,
int expectedStamp,
int newStamp) {
- ReferenceIntegerPair current = atomicRef.get();
+ ReferenceIntegerPair<V> current = atomicRef.get();
return expectedReference == current.reference &&
expectedStamp == current.integer &&
((newReference == current.reference &&
@@ -138,16 +138,16 @@ public class AtomicStampedReference<V> {
* @param newStamp the new value for the stamp
*/
public void set(V newReference, int newStamp) {
- ReferenceIntegerPair current = atomicRef.get();
+ ReferenceIntegerPair<V> current = atomicRef.get();
if (newReference != current.reference || newStamp != current.integer)
atomicRef.set(new ReferenceIntegerPair<V>(newReference, newStamp));
}
/**
* Atomically sets the value of the stamp to the given update value
- * if the current reference is <tt>==</tt> to the expected
+ * if the current reference is {@code ==} to the expected
* reference. Any given invocation of this operation may fail
- * (return <tt>false</tt>) spuriously, but repeated invocation
+ * (return {@code false}) spuriously, but repeated invocation
* when the current value holds the expected value and no other
* thread is also attempting to set the value will eventually
* succeed.
@@ -157,7 +157,7 @@ public class AtomicStampedReference<V> {
* @return true if successful
*/
public boolean attemptStamp(V expectedReference, int newStamp) {
- ReferenceIntegerPair current = atomicRef.get();
+ ReferenceIntegerPair<V> current = atomicRef.get();
return expectedReference == current.reference &&
(newStamp == current.integer ||
atomicRef.compareAndSet(current,
diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/package-info.java b/concurrent/src/main/java/java/util/concurrent/atomic/package-info.java
new file mode 100644
index 0000000..2252e5c
--- /dev/null
+++ b/concurrent/src/main/java/java/util/concurrent/atomic/package-info.java
@@ -0,0 +1,163 @@
+/*
+ * 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
+ */
+
+/**
+ * A small toolkit of classes that support lock-free thread-safe
+ * programming on single variables. In essence, the classes in this
+ * package extend the notion of {@code volatile} values, fields, and
+ * array elements to those that also provide an atomic conditional update
+ * operation of the form:
+ *
+ * <pre>
+ * boolean compareAndSet(expectedValue, updateValue);
+ * </pre>
+ *
+ * <p>This method (which varies in argument types across different
+ * classes) atomically sets a variable to the {@code updateValue} if it
+ * currently holds the {@code expectedValue}, reporting {@code true} on
+ * success. The classes in this package also contain methods to get and
+ * unconditionally set values, as well as a weaker conditional atomic
+ * update operation {@code weakCompareAndSet} described below.
+ *
+ * <p>The specifications of these methods enable implementations to
+ * employ efficient machine-level atomic instructions that are available
+ * on contemporary processors. However on some platforms, support may
+ * entail some form of internal locking. Thus the methods are not
+ * strictly guaranteed to be non-blocking --
+ * a thread may block transiently before performing the operation.
+ *
+ * <p>Instances of classes
+ * {@link java.util.concurrent.atomic.AtomicBoolean},
+ * {@link java.util.concurrent.atomic.AtomicInteger},
+ * {@link java.util.concurrent.atomic.AtomicLong}, and
+ * {@link java.util.concurrent.atomic.AtomicReference}
+ * each provide access and updates to a single variable of the
+ * corresponding type. Each class also provides appropriate utility
+ * methods for that type. For example, classes {@code AtomicLong} and
+ * {@code AtomicInteger} provide atomic increment methods. One
+ * application is to generate sequence numbers, as in:
+ *
+ * <pre>
+ * class Sequencer {
+ * private final AtomicLong sequenceNumber
+ * = new AtomicLong(0);
+ * public long next() {
+ * return sequenceNumber.getAndIncrement();
+ * }
+ * }
+ * </pre>
+ *
+ * <p>The memory effects for accesses and updates of atomics generally
+ * follow the rules for volatiles, as stated in
+ * <a href="http://java.sun.com/docs/books/jls/"> The Java Language
+ * Specification, Third Edition (17.4 Memory Model)</a>:
+ *
+ * <ul>
+ *
+ * <li> {@code get} has the memory effects of reading a
+ * {@code volatile} variable.
+ *
+ * <li> {@code set} has the memory effects of writing (assigning) a
+ * {@code volatile} variable.
+ *
+ * <li>{@code weakCompareAndSet} atomically reads and conditionally
+ * writes a variable but does <em>not</em>
+ * create any happens-before orderings, so provides no guarantees
+ * with respect to previous or subsequent reads and writes of any
+ * variables other than the target of the {@code weakCompareAndSet}.
+ *
+ * <li> {@code compareAndSet}
+ * and all other read-and-update operations such as {@code getAndIncrement}
+ * have the memory effects of both reading and
+ * writing {@code volatile} variables.
+ * </ul>
+ *
+ * <p>In addition to classes representing single values, this package
+ * contains <em>Updater</em> classes that can be used to obtain
+ * {@code compareAndSet} operations on any selected {@code volatile}
+ * field of any selected class.
+ *
+ * {@link java.util.concurrent.atomic.AtomicReferenceFieldUpdater},
+ * {@link java.util.concurrent.atomic.AtomicIntegerFieldUpdater}, and
+ * {@link java.util.concurrent.atomic.AtomicLongFieldUpdater} are
+ * reflection-based utilities that provide access to the associated
+ * field types. These are mainly of use in atomic data structures in
+ * which several {@code volatile} fields of the same node (for
+ * example, the links of a tree node) are independently subject to
+ * atomic updates. These classes enable greater flexibility in how
+ * and when to use atomic updates, at the expense of more awkward
+ * reflection-based setup, less convenient usage, and weaker
+ * guarantees.
+ *
+ * <p>The
+ * {@link java.util.concurrent.atomic.AtomicIntegerArray},
+ * {@link java.util.concurrent.atomic.AtomicLongArray}, and
+ * {@link java.util.concurrent.atomic.AtomicReferenceArray} classes
+ * further extend atomic operation support to arrays of these types.
+ * These classes are also notable in providing {@code volatile} access
+ * semantics for their array elements, which is not supported for
+ * ordinary arrays.
+ *
+ * <a name="Spurious">
+ * <p>The atomic classes also support method {@code weakCompareAndSet},
+ * which has limited applicability. On some platforms, the weak version
+ * may be more efficient than {@code compareAndSet} in the normal case,
+ * but differs in that any given invocation of the
+ * {@code weakCompareAndSet} method may return {@code false}
+ * <em>spuriously</em> (that is, for no apparent reason)</a>. A
+ * {@code false} return means only that the operation may be retried if
+ * desired, relying on the guarantee that repeated invocation when the
+ * variable holds {@code expectedValue} and no other thread is also
+ * attempting to set the variable will eventually succeed. (Such
+ * spurious failures may for example be due to memory contention effects
+ * that are unrelated to whether the expected and current values are
+ * equal.) Additionally {@code weakCompareAndSet} does not provide
+ * ordering guarantees that are usually needed for synchronization
+ * control. However, the method may be useful for updating counters and
+ * statistics when such updates are unrelated to the other
+ * happens-before orderings of a program. When a thread sees an update
+ * to an atomic variable caused by a {@code weakCompareAndSet}, it does
+ * not necessarily see updates to any <em>other</em> variables that
+ * occurred before the {@code weakCompareAndSet}. This may be
+ * acceptable when, for example, updating performance statistics, but
+ * rarely otherwise.
+ *
+ * <p>The {@link java.util.concurrent.atomic.AtomicMarkableReference}
+ * class associates a single boolean with a reference. For example, this
+ * bit might be used inside a data structure to mean that the object
+ * being referenced has logically been deleted.
+ *
+ * The {@link java.util.concurrent.atomic.AtomicStampedReference}
+ * class associates an integer value with a reference. This may be
+ * used for example, to represent version numbers corresponding to
+ * series of updates.
+ *
+ * <p>Atomic classes are designed primarily as building blocks for
+ * implementing non-blocking data structures and related infrastructure
+ * classes. The {@code compareAndSet} method is not a general
+ * replacement for locking. It applies only when critical updates for an
+ * object are confined to a <em>single</em> variable.
+ *
+ * <p>Atomic classes are not general purpose replacements for
+ * {@code java.lang.Integer} and related classes. They do <em>not</em>
+ * define methods such as {@code hashCode} and
+ * {@code compareTo}. (Because atomic variables are expected to be
+ * mutated, they are poor choices for hash table keys.) Additionally,
+ * classes are provided only for those types that are commonly useful in
+ * intended applications. For example, there is no atomic class for
+ * representing {@code byte}. In those infrequent cases where you would
+ * like to do so, you can use an {@code AtomicInteger} to hold
+ * {@code byte} values, and cast appropriately.
+ *
+ * You can also hold floats using
+ * {@link java.lang.Float#floatToIntBits} and
+ * {@link java.lang.Float#intBitsToFloat} conversions, and doubles using
+ * {@link java.lang.Double#doubleToLongBits} and
+ * {@link java.lang.Double#longBitsToDouble} conversions.
+ *
+ * @since 1.5
+ */
+package java.util.concurrent.atomic;
diff --git a/concurrent/src/main/java/java/util/concurrent/atomic/package.html b/concurrent/src/main/java/java/util/concurrent/atomic/package.html
deleted file mode 100644
index bdb15e8..0000000
--- a/concurrent/src/main/java/java/util/concurrent/atomic/package.html
+++ /dev/null
@@ -1,132 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html> <head>
-<title>Atomics</title>
-</head>
-
-<body>
-
-A small toolkit of classes that support lock-free thread-safe
-programming on single variables. In essence, the classes in this
-package extend the notion of <tt>volatile</tt> values, fields, and
-array elements to those that also provide an atomic conditional update
-operation of the form:
-
-<pre>
- boolean compareAndSet(expectedValue, updateValue);
-</pre>
-
-<p> This method (which varies in argument types across different
-classes) atomically sets a variable to the <tt>updateValue</tt> if it
-currently holds the <tt>expectedValue</tt>, reporting <tt>true</tt> on
-success. The classes in this package also contain methods to get and
-unconditionally set values, as well as a weaker conditional atomic
-update operation <tt> weakCompareAndSet</tt>. The weak version may be
-more efficient in the normal case, but differs in that any given
-invocation of <tt>weakCompareAndSet</tt> method may fail, even
-spuriously (that is, for no apparent reason). A <tt>false</tt> return
-means only that the operation may be retried if desired, relying on
-the guarantee that repeated invocation when the variable holds
-<tt>expectedValue</tt> and no other thread is also attempting to set
-the variable will eventually succeed.
-
-<p> The specifications of these methods enable implementations to
-employ efficient machine-level atomic instructions that are available
-on contemporary processors. However on some platforms, support may
-entail some form of internal locking. Thus the methods are not
-strictly guaranteed to be non-blocking --
-a thread may block transiently before performing the operation.
-
-<p> Instances of classes {@link
-java.util.concurrent.atomic.AtomicBoolean}, {@link
-java.util.concurrent.atomic.AtomicInteger}, {@link
-java.util.concurrent.atomic.AtomicLong}, and {@link
-java.util.concurrent.atomic.AtomicReference} each provide access and
-updates to a single variable of the corresponding type. Each class
-also provides appropriate utility methods for that type. For example,
-classes <tt>AtomicLong</tt> and <tt>AtomicInteger</tt> provide atomic
-increment methods. One application is to generate sequence numbers,
-as in:
-
-<pre>
-class Sequencer {
- private AtomicLong sequenceNumber = new AtomicLong(0);
- public long next() { return sequenceNumber.getAndIncrement(); }
-}
-</pre>
-
-<p>The memory effects for accesses and updates of atomics generally follow the
-rules for volatiles:
-
-<ul>
-
- <li> <tt>get</tt> has the memory effects of reading a
-<tt>volatile</tt> variable.
-
- <li> <tt>set</tt> has the memory effects of writing (assigning) a
-<tt>volatile</tt> variable.
-
- <li><tt>weakCompareAndSet</tt> atomically reads and conditionally
- writes a variable, is ordered with respect to other
- memory operations on that variable, but otherwise acts as an
- ordinary non-volatile memory operation.
-
- <li> <tt>compareAndSet</tt>
- and all other read-and-update operations such as <tt>getAndIncrement</tt>
- have the memory effects of both reading and
- writing <tt>volatile</tt> variables.
-</ul>
-
-<p>In addition to classes representing single values, this package
-contains <em>Updater</em> classes that can be used to obtain
-<tt>compareAndSet</tt> operations on any selected <tt>volatile</tt>
-field of any selected class. {@link
-java.util.concurrent.atomic.AtomicReferenceFieldUpdater}, {@link
-java.util.concurrent.atomic.AtomicIntegerFieldUpdater}, and {@link
-java.util.concurrent.atomic.AtomicLongFieldUpdater} are
-reflection-based utilities that provide access to the associated field
-types. These are mainly of use in atomic data structures in which
-several <tt>volatile</tt> fields of the same node (for example, the
-links of a tree node) are independently subject to atomic
-updates. These classes enable greater flexibility in how and when to
-use atomic updates, at the expense of more awkward reflection-based
-setup, less convenient usage, and weaker guarantees.
-
-<p>The {@link java.util.concurrent.atomic.AtomicIntegerArray}, {@link
-java.util.concurrent.atomic.AtomicLongArray}, and {@link
-java.util.concurrent.atomic.AtomicReferenceArray} classes further
-extend atomic operation support to arrays of these types. These
-classes are also notable in providing <tt>volatile</tt> access
-semantics for their array elements, which is not supported for
-ordinary arrays.
-
-<p> The {@link java.util.concurrent.atomic.AtomicMarkableReference}
-class associates a single boolean with a reference. For example, this
-bit might be used inside a data structure to mean that the object
-being referenced has logically been deleted. The {@link
-java.util.concurrent.atomic.AtomicStampedReference} class associates
-an integer value with a reference. This may be used for example, to
-represent version numbers corresponding to series of updates.
-
-<p> Atomic classes are designed primarily as building blocks for
-implementing non-blocking data structures and related infrastructure
-classes. The <tt>compareAndSet</tt> method is not a general
-replacement for locking. It applies only when critical updates for an
-object are confined to a <em>single</em> variable.
-
-<p> Atomic classes are not general purpose replacements for
-<tt>java.lang.Integer</tt> and related classes. They do <em>not</em>
-define methods such as <tt>hashCode</tt> and
-<tt>compareTo</tt>. (Because atomic variables are expected to be
-mutated, they are poor choices for hash table keys.) Additionally,
-classes are provided only for those types that are commonly useful in
-intended applications. For example, there is no atomic class for
-representing <tt>byte</tt>. In those infrequent cases where you would
-like to do so, you can use an <tt>AtomicInteger</tt> to hold
-<tt>byte</tt> values, and cast appropriately. You can also hold floats
-using <tt>Float.floatToIntBits</tt> and <tt>Float.intBitstoFloat</tt>
-conversions, and doubles using <tt>Double.doubleToLongBits</tt> and
-<tt>Double.longBitsToDouble</tt> conversions.
-
-@since Android 1.0
-
-</body> </html>
diff --git a/concurrent/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java b/concurrent/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java
new file mode 100644
index 0000000..bf2fe16
--- /dev/null
+++ b/concurrent/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java
@@ -0,0 +1,56 @@
+/*
+ * 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.locks;
+
+/**
+ * A synchronizer that may be exclusively owned by a thread. This
+ * class provides a basis for creating locks and related synchronizers
+ * that may entail a notion of ownership. The
+ * <tt>AbstractOwnableSynchronizer</tt> class itself does not manage or
+ * use this information. However, subclasses and tools may use
+ * appropriately maintained values to help control and monitor access
+ * and provide diagnostics.
+ *
+ * @author Doug Lea
+ */
+abstract class AbstractOwnableSynchronizer
+ implements java.io.Serializable {
+
+ /** Use serial ID even though all fields transient. */
+ private static final long serialVersionUID = 3737899427754241961L;
+
+ /**
+ * Empty constructor for use by subclasses.
+ */
+ protected AbstractOwnableSynchronizer() { }
+
+ /**
+ * The current owner of exclusive mode synchronization.
+ */
+ private transient Thread exclusiveOwnerThread;
+
+ /**
+ * Sets the thread that currently owns exclusive access. A
+ * <tt>null</tt> argument indicates that no thread owns access.
+ * This method does not otherwise impose any synchronization or
+ * <tt>volatile</tt> field accesses.
+ */
+ protected final void setExclusiveOwnerThread(Thread t) {
+ exclusiveOwnerThread = t;
+ }
+
+ /**
+ * Returns the thread last set by
+ * <tt>setExclusiveOwnerThread</tt>, or <tt>null</tt> if never
+ * set. This method does not otherwise impose any synchronization
+ * or <tt>volatile</tt> field accesses.
+ * @return the owner thread
+ */
+ protected final Thread getExclusiveOwnerThread() {
+ return exclusiveOwnerThread;
+ }
+} \ No newline at end of file
diff --git a/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
index 29e72b5..8b9821e 100644
--- a/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
+++ b/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
@@ -32,7 +32,7 @@ import sun.misc.Unsafe;
* synchronization interface. Instead it defines methods such as
* {@link #acquireInterruptibly} that can be invoked as
* appropriate by concrete locks and related synchronizers to
- * implement their public methods.
+ * implement their public methods.
*
* <p>This class supports either or both a default <em>exclusive</em>
* mode and a <em>shared</em> mode. When acquired in exclusive mode,
@@ -59,14 +59,14 @@ import sun.misc.Unsafe;
* condition, so if this constraint cannot be met, do not use it. The
* behavior of {@link ConditionObject} depends of course on the
* semantics of its synchronizer implementation.
- *
- * <p> This class provides inspection, instrumentation, and monitoring
+ *
+ * <p>This class provides inspection, instrumentation, and monitoring
* methods for the internal queue, as well as similar methods for
* condition objects. These can be exported as desired into classes
* using an <tt>AbstractQueuedSynchronizer</tt> for their
* synchronization mechanics.
*
- * <p> Serialization of this class stores only the underlying atomic
+ * <p>Serialization of this class stores only the underlying atomic
* integer maintaining state, so deserialized objects have empty
* thread queues. Typical subclasses requiring serializability will
* define a <tt>readObject</tt> method that restores this to a known
@@ -74,10 +74,10 @@ import sun.misc.Unsafe;
*
* <h3>Usage</h3>
*
- * <p> To use this class as the basis of a synchronizer, redefine the
+ * <p>To use this class as the basis of a synchronizer, redefine the
* following methods, as applicable, by inspecting and/or modifying
* the synchronization state using {@link #getState}, {@link
- * #setState} and/or {@link #compareAndSetState}:
+ * #setState} and/or {@link #compareAndSetState}:
*
* <ul>
* <li> {@link #tryAcquire}
@@ -94,7 +94,7 @@ import sun.misc.Unsafe;
* means of using this class. All other methods are declared
* <tt>final</tt> because they cannot be independently varied.
*
- * <p> Even though this class is based on an internal FIFO queue, it
+ * <p>Even though this class is based on an internal FIFO queue, it
* does not automatically enforce FIFO acquisition policies. The core
* of exclusive synchronization takes the form:
*
@@ -112,22 +112,17 @@ import sun.misc.Unsafe;
*
* (Shared mode is similar but may involve cascading signals.)
*
- * <p> Because checks in acquire are invoked before enqueuing, a newly
- * acquiring thread may <em>barge</em> ahead of others that are
- * blocked and queued. However, you can, if desired, define
- * <tt>tryAcquire</tt> and/or <tt>tryAcquireShared</tt> to disable
- * barging by internally invoking one or more of the inspection
- * methods. In particular, a strict FIFO lock can define
- * <tt>tryAcquire</tt> to immediately return <tt>false</tt> if {@link
- * #getFirstQueuedThread} does not return the current thread. A
- * normally preferable non-strict fair version can immediately return
- * <tt>false</tt> only if {@link #hasQueuedThreads} returns
- * <tt>true</tt> and <tt>getFirstQueuedThread</tt> is not the current
- * thread; or equivalently, that <tt>getFirstQueuedThread</tt> is both
- * non-null and not the current thread. Further variations are
- * possible.
+ * <p><a name="barging">Because checks in acquire are invoked before
+ * enqueuing, a newly acquiring thread may <em>barge</em> ahead of
+ * others that are blocked and queued. However, you can, if desired,
+ * define <tt>tryAcquire</tt> and/or <tt>tryAcquireShared</tt> to
+ * disable barging by internally invoking one or more of the inspection
+ * methods, thereby providing a <em>fair</em> FIFO acquisition order.
+ * In particular, most fair synchronizers can define <tt>tryAcquire</tt>
+ * to return <tt>false</tt> if predecessors are queued. Other variations
+ * are possible.
*
- * <p> Throughput and scalability are generally highest for the
+ * <p>Throughput and scalability are generally highest for the
* default barging (also known as <em>greedy</em>,
* <em>renouncement</em>, and <em>convoy-avoidance</em>) strategy.
* While this is not guaranteed to be fair or starvation-free, earlier
@@ -144,7 +139,7 @@ import sun.misc.Unsafe;
* and/or {@link #hasQueuedThreads} to only do so if the synchronizer
* is likely not to be contended.
*
- * <p> This class provides an efficient and scalable basis for
+ * <p>This class provides an efficient and scalable basis for
* synchronization in part by specializing its range of use to
* synchronizers that can rely on <tt>int</tt> state, acquire, and
* release parameters, and an internal FIFO wait queue. When this does
@@ -152,7 +147,7 @@ import sun.misc.Unsafe;
* {@link java.util.concurrent.atomic atomic} classes, your own custom
* {@link java.util.Queue} classes, and {@link LockSupport} blocking
* support.
- *
+ *
* <h3>Usage Examples</h3>
*
* <p>Here is a non-reentrant mutual exclusion lock class that uses
@@ -163,56 +158,58 @@ import sun.misc.Unsafe;
* <pre>
* class Mutex implements Lock, java.io.Serializable {
*
- * // Our internal helper class
- * private static class Sync extends AbstractQueuedSynchronizer {
- * // Report whether in locked state
- * protected boolean isHeldExclusively() {
- * return getState() == 1;
- * }
+ * // Our internal helper class
+ * private static class Sync extends AbstractQueuedSynchronizer {
+ * // Report whether in locked state
+ * protected boolean isHeldExclusively() {
+ * return getState() == 1;
+ * }
*
- * // Acquire the lock if state is zero
- * public boolean tryAcquire(int acquires) {
- * assert acquires == 1; // Otherwise unused
- * return compareAndSetState(0, 1);
- * }
+ * // Acquire the lock if state is zero
+ * public boolean tryAcquire(int acquires) {
+ * assert acquires == 1; // Otherwise unused
+ * return compareAndSetState(0, 1);
+ * }
*
- * // Release the lock by setting state to zero
- * protected boolean tryRelease(int releases) {
- * assert releases == 1; // Otherwise unused
- * if (getState() == 0) throw new IllegalMonitorStateException();
- * setState(0);
- * return true;
- * }
- *
- * // Provide a Condition
- * Condition newCondition() { return new ConditionObject(); }
+ * // Release the lock by setting state to zero
+ * protected boolean tryRelease(int releases) {
+ * assert releases == 1; // Otherwise unused
+ * if (getState() == 0) throw new IllegalMonitorStateException();
+ * setState(0);
+ * return true;
+ * }
+ *
+ * // Provide a Condition
+ * Condition newCondition() { return new ConditionObject(); }
*
- * // Deserialize properly
- * private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
- * s.defaultReadObject();
- * setState(0); // reset to unlocked state
- * }
- * }
+ * // Deserialize properly
+ * private void readObject(ObjectInputStream s)
+ * throws IOException, ClassNotFoundException {
+ * s.defaultReadObject();
+ * setState(0); // reset to unlocked state
+ * }
+ * }
*
- * // The sync object does all the hard work. We just forward to it.
- * private final Sync sync = new Sync();
+ * // The sync object does all the hard work. We just forward to it.
+ * private final Sync sync = new Sync();
*
- * public void lock() { sync.acquire(1); }
- * public boolean tryLock() { return sync.tryAcquire(1); }
- * public void unlock() { sync.release(1); }
- * public Condition newCondition() { return sync.newCondition(); }
- * public boolean isLocked() { return sync.isHeldExclusively(); }
- * public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
- * public void lockInterruptibly() throws InterruptedException {
- * sync.acquireInterruptibly(1);
- * }
- * public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
- * return sync.tryAcquireNanos(1, unit.toNanos(timeout));
- * }
+ * public void lock() { sync.acquire(1); }
+ * public boolean tryLock() { return sync.tryAcquire(1); }
+ * public void unlock() { sync.release(1); }
+ * public Condition newCondition() { return sync.newCondition(); }
+ * public boolean isLocked() { return sync.isHeldExclusively(); }
+ * public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
+ * public void lockInterruptibly() throws InterruptedException {
+ * sync.acquireInterruptibly(1);
+ * }
+ * public boolean tryLock(long timeout, TimeUnit unit)
+ * throws InterruptedException {
+ * return sync.tryAcquireNanos(1, unit.toNanos(timeout));
+ * }
* }
* </pre>
*
- * <p> Here is a latch class that is like a {@link CountDownLatch}
+ * <p>Here is a latch class that is like a {@link CountDownLatch}
* except that it only requires a single <tt>signal</tt> to
* fire. Because a latch is non-exclusive, it uses the <tt>shared</tt>
* acquire and release methods.
@@ -220,33 +217,35 @@ import sun.misc.Unsafe;
* <pre>
* class BooleanLatch {
*
- * private static class Sync extends AbstractQueuedSynchronizer {
- * boolean isSignalled() { return getState() != 0; }
+ * private static class Sync extends AbstractQueuedSynchronizer {
+ * boolean isSignalled() { return getState() != 0; }
*
- * protected int tryAcquireShared(int ignore) {
- * return isSignalled()? 1 : -1;
- * }
- *
- * protected boolean tryReleaseShared(int ignore) {
- * setState(1);
- * return true;
- * }
- * }
+ * protected int tryAcquireShared(int ignore) {
+ * return isSignalled()? 1 : -1;
+ * }
*
- * private final Sync sync = new Sync();
- * public boolean isSignalled() { return sync.isSignalled(); }
- * public void signal() { sync.releaseShared(1); }
- * public void await() throws InterruptedException {
- * sync.acquireSharedInterruptibly(1);
- * }
- * }
+ * protected boolean tryReleaseShared(int ignore) {
+ * setState(1);
+ * return true;
+ * }
+ * }
*
+ * private final Sync sync = new Sync();
+ * public boolean isSignalled() { return sync.isSignalled(); }
+ * public void signal() { sync.releaseShared(1); }
+ * public void await() throws InterruptedException {
+ * sync.acquireSharedInterruptibly(1);
+ * }
+ * }
* </pre>
*
* @since 1.5
* @author Doug Lea
*/
-public abstract class AbstractQueuedSynchronizer implements java.io.Serializable {
+public abstract class AbstractQueuedSynchronizer
+ extends AbstractOwnableSynchronizer
+ implements java.io.Serializable {
+
private static final long serialVersionUID = 7373984972572414691L;
/**
@@ -258,7 +257,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
/**
* Wait queue node class.
*
- * <p> The wait queue is a variant of a "CLH" (Craig, Landin, and
+ * <p>The wait queue is a variant of a "CLH" (Craig, Landin, and
* Hagersten) lock queue. CLH locks are normally used for
* spinlocks. We instead use them for blocking synchronizers, but
* use the same basic tactic of holding some of the control
@@ -274,7 +273,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* contender thread may need to rewait.
*
* <p>To enqueue into a CLH lock, you atomically splice it in as new
- * tail. To dequeue, you just set the head field.
+ * tail. To dequeue, you just set the head field.
* <pre>
* +------+ prev +-----+ +-----+
* head | | <---- | | <---- | | tail
@@ -295,7 +294,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* predecessor. For explanation of similar mechanics in the case
* of spin locks, see the papers by Scott and Scherer at
* http://www.cs.rochester.edu/u/scott/synchronization/
- *
+ *
* <p>We also use "next" links to implement blocking mechanics.
* The thread id for each node is kept in its own node, so a
* predecessor signals the next node to wake up by traversing
@@ -312,7 +311,8 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* nodes, we can miss noticing whether a cancelled node is
* ahead or behind us. This is dealt with by always unparking
* successors upon cancellation, allowing them to stabilize on
- * a new predecessor.
+ * a new predecessor, unless we can identify an uncancelled
+ * predecessor who will carry this responsibility.
*
* <p>CLH queues need a dummy header node to get started. But
* we don't create them on construction, because it would be wasted
@@ -334,34 +334,46 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* on the design of this class.
*/
static final class Node {
+ /** Marker to indicate a node is waiting in shared mode */
+ static final Node SHARED = new Node();
+ /** Marker to indicate a node is waiting in exclusive mode */
+ static final Node EXCLUSIVE = null;
+
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
- /** waitStatus value to indicate thread needs unparking */
+ /** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
- /** Marker to indicate a node is waiting in shared mode */
- static final Node SHARED = new Node();
- /** Marker to indicate a node is waiting in exclusive mode */
- static final Node EXCLUSIVE = null;
+ /**
+ * waitStatus value to indicate the next acquireShared should
+ * unconditionally propagate
+ */
+ static final int PROPAGATE = -3;
/**
* Status field, taking on only the values:
- * SIGNAL: The successor of this node is (or will soon be)
- * blocked (via park), so the current node must
- * unpark its successor when it releases or
+ * SIGNAL: The successor of this node is (or will soon be)
+ * blocked (via park), so the current node must
+ * unpark its successor when it releases or
* cancels. To avoid races, acquire methods must
- * first indicate they need a signal,
- * then retry the atomic acquire, and then,
+ * first indicate they need a signal,
+ * then retry the atomic acquire, and then,
* on failure, block.
- * CANCELLED: Node is cancelled due to timeout or interrupt
+ * CANCELLED: This node is cancelled due to timeout or interrupt.
* Nodes never leave this state. In particular,
* a thread with cancelled node never again blocks.
- * CONDITION: Node is currently on a condition queue
- * It will not be used as a sync queue node until
- * transferred. (Use of this value here
- * has nothing to do with the other uses
- * of the field, but simplifies mechanics.)
+ * CONDITION: This node is currently on a condition queue.
+ * It will not be used as a sync queue node
+ * until transferred, at which time the status
+ * will be set to 0. (Use of this value here has
+ * nothing to do with the other uses of the
+ * field, but simplifies mechanics.)
+ * PROPAGATE: A releaseShared should be propagated to other
+ * nodes. This is set (for head node only) in
+ * doReleaseShared to ensure propagation
+ * continues, even if other operations have
+ * since intervened.
* 0: None of the above
*
* The values are arranged numerically to simplify use.
@@ -370,8 +382,8 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* values, just for sign.
*
* The field is initialized to 0 for normal sync nodes, and
- * CONDITION for condition nodes. It is modified only using
- * CAS.
+ * CONDITION for condition nodes. It is modified using CAS
+ * (or when possible, unconditional volatile writes).
*/
volatile int waitStatus;
@@ -390,25 +402,26 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
/**
* Link to the successor node that the current node/thread
- * unparks upon release. Assigned once during enqueuing, and
- * nulled out (for sake of GC) when no longer needed. Upon
- * cancellation, we cannot adjust this field, but can notice
- * status and bypass the node if cancelled. The enq operation
- * does not assign next field of a predecessor until after
- * attachment, so seeing a null next field does not
- * necessarily mean that node is at end of queue. However, if
- * a next field appears to be null, we can scan prev's from
- * the tail to double-check.
+ * unparks upon release. Assigned during enqueuing, adjusted
+ * when bypassing cancelled predecessors, and nulled out (for
+ * sake of GC) when dequeued. The enq operation does not
+ * assign next field of a predecessor until after attachment,
+ * so seeing a null next field does not necessarily mean that
+ * node is at end of queue. However, if a next field appears
+ * to be null, we can scan prev's from the tail to
+ * double-check. The next field of cancelled nodes is set to
+ * point to the node itself instead of null, to make life
+ * easier for isOnSyncQueue.
*/
volatile Node next;
/**
* The thread that enqueued this node. Initialized on
- * construction and nulled out after use.
+ * construction and nulled out after use.
*/
volatile Thread thread;
- /**
+ /**
* Link to next node waiting on condition, or the special
* value SHARED. Because condition queues are accessed only
* when holding in exclusive mode, we just need a simple
@@ -428,14 +441,16 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
}
/**
- * Returns previous node, or throws NullPointerException if
- * null. Use when predecessor cannot be null.
+ * Returns previous node, or throws NullPointerException if null.
+ * Use when predecessor cannot be null. The null check could
+ * be elided, but is present to help the VM.
+ *
* @return the predecessor of this node
*/
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
- throw new NullPointerException();
+ throw new NullPointerException();
else
return p;
}
@@ -450,11 +465,11 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
- this.thread = thread;
+ this.thread = thread;
}
}
- /**
+ /**
* Head of the wait queue, lazily initialized. Except for
* initialization, it is modified only via method setHead. Note:
* If head exists, its waitStatus is guaranteed not to be
@@ -462,11 +477,11 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
*/
private transient volatile Node head;
- /**
+ /**
* Tail of the wait queue, lazily initialized. Modified only via
* method enq to add new wait node.
*/
- private transient volatile Node tail;
+ private transient volatile Node tail;
/**
* The synchronization state.
@@ -496,10 +511,11 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* value if the current state value equals the expected value.
* This operation has memory semantics of a <tt>volatile</tt> read
* and write.
+ *
* @param expect the expected value
* @param update the new value
- * @return true if successful. False return indicates that
- * the actual value was not equal to the expected value.
+ * @return true if successful. False return indicates that the actual
+ * value was not equal to the expected value.
*/
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
@@ -509,7 +525,14 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
// Queuing utilities
/**
- * Insert node into queue, initializing if necessary. See picture above.
+ * The number of nanoseconds for which it is faster to spin
+ * rather than to use timed park. A rough estimate suffices
+ * to improve responsiveness with very short timeouts.
+ */
+ static final long spinForTimeoutThreshold = 1000L;
+
+ /**
+ * Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
@@ -517,27 +540,21 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
- Node h = new Node(); // Dummy header
- h.next = node;
- node.prev = h;
- if (compareAndSetHead(h)) {
- tail = node;
- return h;
- }
- }
- else {
- node.prev = t;
+ if (compareAndSetHead(new Node()))
+ tail = head;
+ } else {
+ node.prev = t;
if (compareAndSetTail(t, node)) {
- t.next = node;
- return t;
+ t.next = node;
+ return t;
}
}
}
}
/**
- * Create and enq node for given thread and mode
- * @param current the thread
+ * Creates and enqueues node for current thread and given mode.
+ *
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
* @return the new node
*/
@@ -546,9 +563,9 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
- node.prev = pred;
+ node.prev = pred;
if (compareAndSetTail(pred, node)) {
- pred.next = node;
+ pred.next = node;
return node;
}
}
@@ -557,79 +574,166 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
}
/**
- * Set head of queue to be node, thus dequeuing. Called only by
+ * Sets head of queue to be node, thus dequeuing. Called only by
* acquire methods. Also nulls out unused fields for sake of GC
* and to suppress unnecessary signals and traversals.
- * @param node the node
+ *
+ * @param node the node
*/
private void setHead(Node node) {
head = node;
node.thread = null;
- node.prev = null;
+ node.prev = null;
}
/**
- * Wake up node's successor, if one exists.
+ * Wakes up node's successor, if one exists.
+ *
* @param node the node
*/
private void unparkSuccessor(Node node) {
/*
- * Try to clear status in anticipation of signalling. It is
- * OK if this fails or if status is changed by waiting thread.
+ * If status is negative (i.e., possibly needing signal) try
+ * to clear in anticipation of signalling. It is OK if this
+ * fails or if status is changed by waiting thread.
*/
- compareAndSetWaitStatus(node, Node.SIGNAL, 0);
-
+ int ws = node.waitStatus;
+ if (ws < 0)
+ compareAndSetWaitStatus(node, ws, 0);
+
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
- Thread thread;
Node s = node.next;
- if (s != null && s.waitStatus <= 0)
- thread = s.thread;
- else {
- thread = null;
- for (s = tail; s != null && s != node; s = s.prev)
- if (s.waitStatus <= 0)
- thread = s.thread;
+ if (s == null || s.waitStatus > 0) {
+ s = null;
+ for (Node t = tail; t != null && t != node; t = t.prev)
+ if (t.waitStatus <= 0)
+ s = t;
}
- LockSupport.unpark(thread);
+ if (s != null)
+ LockSupport.unpark(s.thread);
}
/**
- * Set head of queue, and check if successor may be waiting
- * in shared mode, if so propagating if propagate > 0.
- * @param pred the node holding waitStatus for node
- * @param node the node
+ * Release action for shared mode -- signal successor and ensure
+ * propagation. (Note: For exclusive mode, release just amounts
+ * to calling unparkSuccessor of head if it needs signal.)
+ */
+ private void doReleaseShared() {
+ /*
+ * Ensure that a release propagates, even if there are other
+ * in-progress acquires/releases. This proceeds in the usual
+ * way of trying to unparkSuccessor of head if it needs
+ * signal. But if it does not, status is set to PROPAGATE to
+ * ensure that upon release, propagation continues.
+ * Additionally, we must loop in case a new node is added
+ * while we are doing this. Also, unlike other uses of
+ * unparkSuccessor, we need to know if CAS to reset status
+ * fails, if so rechecking.
+ */
+ for (;;) {
+ Node h = head;
+ if (h != null && h != tail) {
+ int ws = h.waitStatus;
+ if (ws == Node.SIGNAL) {
+ if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
+ continue; // loop to recheck cases
+ unparkSuccessor(h);
+ }
+ else if (ws == 0 &&
+ !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
+ continue; // loop on failed CAS
+ }
+ if (h == head) // loop if head changed
+ break;
+ }
+ }
+
+ /**
+ * Sets head of queue, and checks if successor may be waiting
+ * in shared mode, if so propagating if either propagate > 0 or
+ * PROPAGATE status was set.
+ *
+ * @param node the node
* @param propagate the return value from a tryAcquireShared
*/
private void setHeadAndPropagate(Node node, int propagate) {
+ Node h = head; // Record old head for check below
setHead(node);
- if (propagate > 0 && node.waitStatus != 0) {
- /*
- * Don't bother fully figuring out successor. If it
- * looks null, call unparkSuccessor anyway to be safe.
- */
- Node s = node.next;
+ /*
+ * Try to signal next queued node if:
+ * Propagation was indicated by caller,
+ * or was recorded (as h.waitStatus) by a previous operation
+ * (note: this uses sign-check of waitStatus because
+ * PROPAGATE status may transition to SIGNAL.)
+ * and
+ * The next node is waiting in shared mode,
+ * or we don't know, because it appears null
+ *
+ * The conservatism in both of these checks may cause
+ * unnecessary wake-ups, but only when there are multiple
+ * racing acquires/releases, so most need signals now or soon
+ * anyway.
+ */
+ if (propagate > 0 || h == null || h.waitStatus < 0) {
+ Node s = node.next;
if (s == null || s.isShared())
- unparkSuccessor(node);
+ doReleaseShared();
}
}
// Utilities for various versions of acquire
/**
- * Cancel an ongoing attempt to acquire.
+ * Cancels an ongoing attempt to acquire.
+ *
* @param node the node
*/
private void cancelAcquire(Node node) {
- if (node != null) { // Ignore if node doesn't exist
- node.thread = null;
- // Can use unconditional write instead of CAS here
- node.waitStatus = Node.CANCELLED;
- unparkSuccessor(node);
+ // Ignore if node doesn't exist
+ if (node == null)
+ return;
+
+ node.thread = null;
+
+ // Skip cancelled predecessors
+ Node pred = node.prev;
+ while (pred.waitStatus > 0)
+ node.prev = pred = pred.prev;
+
+ // predNext is the apparent node to unsplice. CASes below will
+ // fail if not, in which case, we lost race vs another cancel
+ // or signal, so no further action is necessary.
+ Node predNext = pred.next;
+
+ // Can use unconditional write instead of CAS here.
+ // After this atomic step, other Nodes can skip past us.
+ // Before, we are free of interference from other threads.
+ node.waitStatus = Node.CANCELLED;
+
+ // If we are the tail, remove ourselves.
+ if (node == tail && compareAndSetTail(node, pred)) {
+ compareAndSetNext(pred, predNext, null);
+ } else {
+ // If successor needs signal, try to set pred's next-link
+ // so it will get one. Otherwise wake it up to propagate.
+ int ws;
+ if (pred != head &&
+ ((ws = pred.waitStatus) == Node.SIGNAL ||
+ (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
+ pred.thread != null) {
+ Node next = node.next;
+ if (next != null && next.waitStatus <= 0)
+ compareAndSetNext(pred, predNext, next);
+ } else {
+ unparkSuccessor(node);
+ }
+
+ node.next = node; // help GC
}
}
@@ -637,31 +741,36 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* Checks and updates status for a node that failed to acquire.
* Returns true if thread should block. This is the main signal
* control in all acquire loops. Requires that pred == node.prev
+ *
* @param pred node's predecessor holding status
- * @param node the node
- * @return true if thread should block
+ * @param node the node
+ * @return {@code true} if thread should block
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
- int s = pred.waitStatus;
- if (s < 0)
+ int ws = pred.waitStatus;
+ if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
- * to signal it, so it can safely park
+ * to signal it, so it can safely park.
*/
return true;
- if (s > 0)
+ if (ws > 0) {
/*
- * Predecessor was cancelled. Move up to its predecessor
- * and indicate retry.
+ * Predecessor was cancelled. Skip over predecessors and
+ * indicate retry.
*/
- node.prev = pred.prev;
- else
+ do {
+ node.prev = pred = pred.prev;
+ } while (pred.waitStatus > 0);
+ pred.next = node;
+ } else {
/*
- * Indicate that we need a signal, but don't park yet. Caller
- * will need to retry to make sure it cannot acquire before
- * parking.
+ * waitStatus must be 0 or PROPAGATE. Indicate that we
+ * need a signal, but don't park yet. Caller will need to
+ * retry to make sure it cannot acquire before parking.
*/
- compareAndSetWaitStatus(pred, 0, Node.SIGNAL);
+ compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
+ }
return false;
}
@@ -674,9 +783,10 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
/**
* Convenience method to park and then check if interrupted
- * @return true if interrupted
+ *
+ * @return {@code true} if interrupted
*/
- private static boolean parkAndCheckInterrupt() {
+ private final boolean parkAndCheckInterrupt() {
LockSupport.park();
return Thread.interrupted();
}
@@ -687,17 +797,19 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* different. Only a little bit of factoring is possible due to
* interactions of exception mechanics (including ensuring that we
* cancel if tryAcquire throws exception) and other control, at
- * least not without hurting performance too much.
+ * least not without hurting performance too much.
*/
/**
- * Acquire in exclusive uninterruptible mode for thread already in
+ * Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
+ *
* @param node the node
* @param arg the acquire argument
- * @return true if interrupted while waiting
+ * @return {@code true} if interrupted while waiting
*/
final boolean acquireQueued(final Node node, int arg) {
+ boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
@@ -705,92 +817,91 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
+ failed = false;
return interrupted;
}
- if (shouldParkAfterFailedAcquire(p, node) &&
- parkAndCheckInterrupt())
+ if (shouldParkAfterFailedAcquire(p, node) &&
+ parkAndCheckInterrupt())
interrupted = true;
}
- } catch (RuntimeException ex) {
- cancelAcquire(node);
- throw ex;
+ } finally {
+ if (failed)
+ cancelAcquire(node);
}
}
- /**
- * Acquire in exclusive interruptible mode
+ /**
+ * Acquires in exclusive interruptible mode.
* @param arg the acquire argument
*/
- private void doAcquireInterruptibly(int arg)
+ private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
+ boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
+ failed = false;
return;
}
- if (shouldParkAfterFailedAcquire(p, node) &&
- parkAndCheckInterrupt())
- break;
+ if (shouldParkAfterFailedAcquire(p, node) &&
+ parkAndCheckInterrupt())
+ throw new InterruptedException();
}
- } catch (RuntimeException ex) {
- cancelAcquire(node);
- throw ex;
+ } finally {
+ if (failed)
+ cancelAcquire(node);
}
- // Arrive here only if interrupted
- cancelAcquire(node);
- throw new InterruptedException();
}
- /**
- * Acquire in exclusive timed mode
+ /**
+ * Acquires in exclusive timed mode.
+ *
* @param arg the acquire argument
* @param nanosTimeout max wait time
- * @return true if acquired
+ * @return {@code true} if acquired
*/
- private boolean doAcquireNanos(int arg, long nanosTimeout)
+ private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
long lastTime = System.nanoTime();
final Node node = addWaiter(Node.EXCLUSIVE);
+ boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
+ failed = false;
return true;
}
- if (nanosTimeout <= 0) {
- cancelAcquire(node);
+ if (nanosTimeout <= 0)
return false;
- }
- if (shouldParkAfterFailedAcquire(p, node)) {
+ if (shouldParkAfterFailedAcquire(p, node) &&
+ nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(nanosTimeout);
- if (Thread.interrupted())
- break;
- long now = System.nanoTime();
- nanosTimeout -= now - lastTime;
- lastTime = now;
- }
+ long now = System.nanoTime();
+ nanosTimeout -= now - lastTime;
+ lastTime = now;
+ if (Thread.interrupted())
+ throw new InterruptedException();
}
- } catch (RuntimeException ex) {
- cancelAcquire(node);
- throw ex;
+ } finally {
+ if (failed)
+ cancelAcquire(node);
}
- // Arrive here only if interrupted
- cancelAcquire(node);
- throw new InterruptedException();
}
- /**
- * Acquire in shared uninterruptible mode
+ /**
+ * Acquires in shared uninterruptible mode.
* @param arg the acquire argument
*/
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
+ boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
@@ -802,26 +913,28 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
p.next = null; // help GC
if (interrupted)
selfInterrupt();
+ failed = false;
return;
}
}
- if (shouldParkAfterFailedAcquire(p, node) &&
- parkAndCheckInterrupt())
+ if (shouldParkAfterFailedAcquire(p, node) &&
+ parkAndCheckInterrupt())
interrupted = true;
}
- } catch (RuntimeException ex) {
- cancelAcquire(node);
- throw ex;
+ } finally {
+ if (failed)
+ cancelAcquire(node);
}
}
- /**
- * Acquire in shared interruptible mode
+ /**
+ * Acquires in shared interruptible mode.
* @param arg the acquire argument
*/
- private void doAcquireSharedInterruptibly(int arg)
+ private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
+ boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
@@ -830,33 +943,33 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
+ failed = false;
return;
}
}
- if (shouldParkAfterFailedAcquire(p, node) &&
- parkAndCheckInterrupt())
- break;
+ if (shouldParkAfterFailedAcquire(p, node) &&
+ parkAndCheckInterrupt())
+ throw new InterruptedException();
}
- } catch (RuntimeException ex) {
- cancelAcquire(node);
- throw ex;
+ } finally {
+ if (failed)
+ cancelAcquire(node);
}
- // Arrive here only if interrupted
- cancelAcquire(node);
- throw new InterruptedException();
}
- /**
- * Acquire in shared timed mode
+ /**
+ * Acquires in shared timed mode.
+ *
* @param arg the acquire argument
* @param nanosTimeout max wait time
- * @return true if acquired
+ * @return {@code true} if acquired
*/
- private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
+ private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
long lastTime = System.nanoTime();
final Node node = addWaiter(Node.SHARED);
+ boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
@@ -865,32 +978,28 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
+ failed = false;
return true;
}
}
- if (nanosTimeout <= 0) {
- cancelAcquire(node);
+ if (nanosTimeout <= 0)
return false;
- }
- if (shouldParkAfterFailedAcquire(p, node)) {
+ if (shouldParkAfterFailedAcquire(p, node) &&
+ nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(nanosTimeout);
- if (Thread.interrupted())
- break;
- long now = System.nanoTime();
- nanosTimeout -= now - lastTime;
- lastTime = now;
- }
+ long now = System.nanoTime();
+ nanosTimeout -= now - lastTime;
+ lastTime = now;
+ if (Thread.interrupted())
+ throw new InterruptedException();
}
- } catch (RuntimeException ex) {
- cancelAcquire(node);
- throw ex;
+ } finally {
+ if (failed)
+ cancelAcquire(node);
}
- // Arrive here only if interrupted
- cancelAcquire(node);
- throw new InterruptedException();
}
- // Main exported methods
+ // Main exported methods
/**
* Attempts to acquire in exclusive mode. This method should query
@@ -901,22 +1010,21 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* acquire. If this method reports failure, the acquire method
* may queue the thread, if it is not already queued, until it is
* signalled by a release from some other thread. This can be used
- * to implement method {@link Lock#tryLock()}.
+ * to implement method {@link Lock#tryLock()}.
*
* <p>The default
- * implementation throws {@link UnsupportedOperationException}
+ * implementation throws {@link UnsupportedOperationException}.
*
- * @param arg the acquire argument. This value
- * is always the one passed to an acquire method,
- * or is the value saved on entry to a condition wait.
- * The value is otherwise uninterpreted and can represent anything
- * you like.
- * @return true if successful. Upon success, this object has been
- * acquired.
- * @throws IllegalMonitorStateException if acquiring would place
- * this synchronizer in an illegal state. This exception must be
- * thrown in a consistent fashion for synchronization to work
- * correctly.
+ * @param arg the acquire argument. This value is always the one
+ * passed to an acquire method, or is the value saved on entry
+ * to a condition wait. The value is otherwise uninterpreted
+ * and can represent anything you like.
+ * @return {@code true} if successful. Upon success, this object has
+ * been acquired.
+ * @throws IllegalMonitorStateException if acquiring would place this
+ * synchronizer in an illegal state. This exception must be
+ * thrown in a consistent fashion for synchronization to work
+ * correctly.
* @throws UnsupportedOperationException if exclusive mode is not supported
*/
protected boolean tryAcquire(int arg) {
@@ -925,23 +1033,24 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
/**
* Attempts to set the state to reflect a release in exclusive
- * mode. <p>This method is always invoked by the thread
- * performing release.
+ * mode.
+ *
+ * <p>This method is always invoked by the thread performing release.
*
* <p>The default implementation throws
- * {@link UnsupportedOperationException}
- * @param arg the release argument. This value
- * is always the one passed to a release method,
- * or the current state value upon entry to a condition wait.
- * The value is otherwise uninterpreted and can represent anything
- * you like.
- * @return <tt>true</tt> if this object is now in a fully released state,
- * so that any waiting threads may attempt to acquire; and <tt>false</tt>
- * otherwise.
- * @throws IllegalMonitorStateException if releasing would place
- * this synchronizer in an illegal state. This exception must be
- * thrown in a consistent fashion for synchronization to work
- * correctly.
+ * {@link UnsupportedOperationException}.
+ *
+ * @param arg the release argument. This value is always the one
+ * passed to a release method, or the current state value upon
+ * entry to a condition wait. The value is otherwise
+ * uninterpreted and can represent anything you like.
+ * @return {@code true} if this object is now in a fully released
+ * state, so that any waiting threads may attempt to acquire;
+ * and {@code false} otherwise.
+ * @throws IllegalMonitorStateException if releasing would place this
+ * synchronizer in an illegal state. This exception must be
+ * thrown in a consistent fashion for synchronization to work
+ * correctly.
* @throws UnsupportedOperationException if exclusive mode is not supported
*/
protected boolean tryRelease(int arg) {
@@ -951,7 +1060,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
/**
* Attempts to acquire in shared mode. This method should query if
* the state of the object permits it to be acquired in the shared
- * mode, and if so to acquire it.
+ * mode, and if so to acquire it.
*
* <p>This method is always invoked by the thread performing
* acquire. If this method reports failure, the acquire method
@@ -959,24 +1068,25 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* signalled by a release from some other thread.
*
* <p>The default implementation throws {@link
- * UnsupportedOperationException}
+ * UnsupportedOperationException}.
*
- * @param arg the acquire argument. This value
- * is always the one passed to an acquire method,
- * or is the value saved on entry to a condition wait.
- * The value is otherwise uninterpreted and can represent anything
- * you like.
- * @return a negative value on failure, zero on exclusive success,
- * and a positive value if non-exclusively successful, in which
- * case a subsequent waiting thread must check
- * availability. (Support for three different return values
- * enables this method to be used in contexts where acquires only
- * sometimes act exclusively.) Upon success, this object has been
- * acquired.
- * @throws IllegalMonitorStateException if acquiring would place
- * this synchronizer in an illegal state. This exception must be
- * thrown in a consistent fashion for synchronization to work
- * correctly.
+ * @param arg the acquire argument. This value is always the one
+ * passed to an acquire method, or is the value saved on entry
+ * to a condition wait. The value is otherwise uninterpreted
+ * and can represent anything you like.
+ * @return a negative value on failure; zero if acquisition in shared
+ * mode succeeded but no subsequent shared-mode acquire can
+ * succeed; and a positive value if acquisition in shared
+ * mode succeeded and subsequent shared-mode acquires might
+ * also succeed, in which case a subsequent waiting thread
+ * must check availability. (Support for three different
+ * return values enables this method to be used in contexts
+ * where acquires only sometimes act exclusively.) Upon
+ * success, this object has been acquired.
+ * @throws IllegalMonitorStateException if acquiring would place this
+ * synchronizer in an illegal state. This exception must be
+ * thrown in a consistent fashion for synchronization to work
+ * correctly.
* @throws UnsupportedOperationException if shared mode is not supported
*/
protected int tryAcquireShared(int arg) {
@@ -985,21 +1095,23 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
/**
* Attempts to set the state to reflect a release in shared mode.
+ *
* <p>This method is always invoked by the thread performing release.
- * <p> The default implementation throws
- * {@link UnsupportedOperationException}
- * @param arg the release argument. This value
- * is always the one passed to a release method,
- * or the current state value upon entry to a condition wait.
- * The value is otherwise uninterpreted and can represent anything
- * you like.
- * @return <tt>true</tt> if this object is now in a fully released state,
- * so that any waiting threads may attempt to acquire; and <tt>false</tt>
- * otherwise.
- * @throws IllegalMonitorStateException if releasing would place
- * this synchronizer in an illegal state. This exception must be
- * thrown in a consistent fashion for synchronization to work
- * correctly.
+ *
+ * <p>The default implementation throws
+ * {@link UnsupportedOperationException}.
+ *
+ * @param arg the release argument. This value is always the one
+ * passed to a release method, or the current state value upon
+ * entry to a condition wait. The value is otherwise
+ * uninterpreted and can represent anything you like.
+ * @return {@code true} if this release of shared mode may permit a
+ * waiting acquire (shared or exclusive) to succeed; and
+ * {@code false} otherwise
+ * @throws IllegalMonitorStateException if releasing would place this
+ * synchronizer in an illegal state. This exception must be
+ * thrown in a consistent fashion for synchronization to work
+ * correctly.
* @throws UnsupportedOperationException if shared mode is not supported
*/
protected boolean tryReleaseShared(int arg) {
@@ -1007,17 +1119,18 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
}
/**
- * Returns true if synchronization is held exclusively with respect
- * to the current (calling) thread. This method is invoked
+ * Returns {@code true} if synchronization is held exclusively with
+ * respect to the current (calling) thread. This method is invoked
* upon each call to a non-waiting {@link ConditionObject} method.
* (Waiting methods instead invoke {@link #release}.)
+ *
* <p>The default implementation throws {@link
* UnsupportedOperationException}. This method is invoked
* internally only within {@link ConditionObject} methods, so need
* not be defined if conditions are not used.
*
- * @return true if synchronization is held exclusively;
- * else false
+ * @return {@code true} if synchronization is held exclusively;
+ * {@code false} otherwise
* @throws UnsupportedOperationException if conditions are not supported
*/
protected boolean isHeldExclusively() {
@@ -1030,12 +1143,12 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* returning on success. Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
* #tryAcquire} until success. This method can be used
- * to implement method {@link Lock#lock}
- * @param arg the acquire argument.
- * This value is conveyed to {@link #tryAcquire} but is
- * otherwise uninterpreted and can represent anything
- * you like.
- */
+ * to implement method {@link Lock#lock}.
+ *
+ * @param arg the acquire argument. This value is conveyed to
+ * {@link #tryAcquire} but is otherwise uninterpreted and
+ * can represent anything you like.
+ */
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
@@ -1049,11 +1162,11 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* success. Otherwise the thread is queued, possibly repeatedly
* blocking and unblocking, invoking {@link #tryAcquire}
* until success or the thread is interrupted. This method can be
- * used to implement method {@link Lock#lockInterruptibly}
- * @param arg the acquire argument.
- * This value is conveyed to {@link #tryAcquire} but is
- * otherwise uninterpreted and can represent anything
- * you like.
+ * used to implement method {@link Lock#lockInterruptibly}.
+ *
+ * @param arg the acquire argument. This value is conveyed to
+ * {@link #tryAcquire} but is otherwise uninterpreted and
+ * can represent anything you like.
* @throws InterruptedException if the current thread is interrupted
*/
public final void acquireInterruptibly(int arg) throws InterruptedException {
@@ -1072,35 +1185,35 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* {@link #tryAcquire} until success or the thread is interrupted
* or the timeout elapses. This method can be used to implement
* method {@link Lock#tryLock(long, TimeUnit)}.
- * @param arg the acquire argument.
- * This value is conveyed to {@link #tryAcquire} but is
- * otherwise uninterpreted and can represent anything
- * you like.
+ *
+ * @param arg the acquire argument. This value is conveyed to
+ * {@link #tryAcquire} but is otherwise uninterpreted and
+ * can represent anything you like.
* @param nanosTimeout the maximum number of nanoseconds to wait
- * @return true if acquired; false if timed out
+ * @return {@code true} if acquired; {@code false} if timed out
* @throws InterruptedException if the current thread is interrupted
*/
- public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {
- if (Thread.interrupted())
- throw new InterruptedException();
- return tryAcquire(arg) ||
- doAcquireNanos(arg, nanosTimeout);
- }
+ public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {
+ if (Thread.interrupted())
+ throw new InterruptedException();
+ return tryAcquire(arg) ||
+ doAcquireNanos(arg, nanosTimeout);
+ }
/**
* Releases in exclusive mode. Implemented by unblocking one or
* more threads if {@link #tryRelease} returns true.
- * This method can be used to implement method {@link Lock#unlock}
- * @param arg the release argument.
- * This value is conveyed to {@link #tryRelease} but is
- * otherwise uninterpreted and can represent anything
- * you like.
- * @return the value returned from {@link #tryRelease}
+ * This method can be used to implement method {@link Lock#unlock}.
+ *
+ * @param arg the release argument. This value is conveyed to
+ * {@link #tryRelease} but is otherwise uninterpreted and
+ * can represent anything you like.
+ * @return the value returned from {@link #tryRelease}
*/
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
- if (h != null && h.waitStatus != 0)
+ if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
@@ -1112,11 +1225,11 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* first invoking at least once {@link #tryAcquireShared},
* returning on success. Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
- * #tryAcquireShared} until success.
- * @param arg the acquire argument.
- * This value is conveyed to {@link #tryAcquireShared} but is
- * otherwise uninterpreted and can represent anything
- * you like.
+ * #tryAcquireShared} until success.
+ *
+ * @param arg the acquire argument. This value is conveyed to
+ * {@link #tryAcquireShared} but is otherwise uninterpreted
+ * and can represent anything you like.
*/
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
@@ -1129,8 +1242,8 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* {@link #tryAcquireShared}, returning on success. Otherwise the
* thread is queued, possibly repeatedly blocking and unblocking,
* invoking {@link #tryAcquireShared} until success or the thread
- * is interrupted.
- * @param arg the acquire argument.
+ * is interrupted.
+ * @param arg the acquire argument
* This value is conveyed to {@link #tryAcquireShared} but is
* otherwise uninterpreted and can represent anything
* you like.
@@ -1141,7 +1254,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
- }
+ }
/**
* Attempts to acquire in shared mode, aborting if interrupted, and
@@ -1151,35 +1264,33 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* thread is queued, possibly repeatedly blocking and unblocking,
* invoking {@link #tryAcquireShared} until success or the thread
* is interrupted or the timeout elapses.
- * @param arg the acquire argument.
- * This value is conveyed to {@link #tryAcquireShared} but is
- * otherwise uninterpreted and can represent anything
- * you like.
+ *
+ * @param arg the acquire argument. This value is conveyed to
+ * {@link #tryAcquireShared} but is otherwise uninterpreted
+ * and can represent anything you like.
* @param nanosTimeout the maximum number of nanoseconds to wait
- * @return true if acquired; false if timed out
+ * @return {@code true} if acquired; {@code false} if timed out
* @throws InterruptedException if the current thread is interrupted
*/
- public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException {
- if (Thread.interrupted())
- throw new InterruptedException();
- return tryAcquireShared(arg) >= 0 ||
- doAcquireSharedNanos(arg, nanosTimeout);
- }
+ public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException {
+ if (Thread.interrupted())
+ throw new InterruptedException();
+ return tryAcquireShared(arg) >= 0 ||
+ doAcquireSharedNanos(arg, nanosTimeout);
+ }
/**
* Releases in shared mode. Implemented by unblocking one or more
- * threads if {@link #tryReleaseShared} returns true.
- * @param arg the release argument.
- * This value is conveyed to {@link #tryReleaseShared} but is
- * otherwise uninterpreted and can represent anything
- * you like.
- * @return the value returned from {@link #tryReleaseShared}
+ * threads if {@link #tryReleaseShared} returns true.
+ *
+ * @param arg the release argument. This value is conveyed to
+ * {@link #tryReleaseShared} but is otherwise uninterpreted
+ * and can represent anything you like.
+ * @return the value returned from {@link #tryReleaseShared}
*/
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
- Node h = head;
- if (h != null && h.waitStatus != 0)
- unparkSuccessor(h);
+ doReleaseShared();
return true;
}
return false;
@@ -1190,16 +1301,15 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
/**
* Queries whether any threads are waiting to acquire. Note that
* because cancellations due to interrupts and timeouts may occur
- * at any time, a <tt>true</tt> return does not guarantee that any
+ * at any time, a {@code true} return does not guarantee that any
* other thread will ever acquire.
*
- * <p> In this implementation, this operation returns in
+ * <p>In this implementation, this operation returns in
* constant time.
*
- * @return true if there may be other threads waiting to acquire
- * the lock.
+ * @return {@code true} if there may be other threads waiting to acquire
*/
- public final boolean hasQueuedThreads() {
+ public final boolean hasQueuedThreads() {
return head != tail;
}
@@ -1207,10 +1317,10 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* Queries whether any threads have ever contended to acquire this
* synchronizer; that is if an acquire method has ever blocked.
*
- * <p> In this implementation, this operation returns in
+ * <p>In this implementation, this operation returns in
* constant time.
*
- * @return true if there has ever been contention
+ * @return {@code true} if there has ever been contention
*/
public final boolean hasContended() {
return head != null;
@@ -1218,18 +1328,18 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
/**
* Returns the first (longest-waiting) thread in the queue, or
- * <tt>null</tt> if no threads are currently queued.
+ * {@code null} if no threads are currently queued.
*
- * <p> In this implementation, this operation normally returns in
+ * <p>In this implementation, this operation normally returns in
* constant time, but may iterate upon contention if other threads are
* concurrently modifying the queue.
*
* @return the first (longest-waiting) thread in the queue, or
- * <tt>null</tt> if no threads are currently queued.
+ * {@code null} if no threads are currently queued
*/
public final Thread getFirstQueuedThread() {
// handle only fast path, else relay
- return (head == tail)? null : fullGetFirstQueuedThread();
+ return (head == tail) ? null : fullGetFirstQueuedThread();
}
/**
@@ -1237,57 +1347,49 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
*/
private Thread fullGetFirstQueuedThread() {
/*
- * This loops only if the queue changes while we read sets of
- * fields.
+ * The first node is normally head.next. Try to get its
+ * thread field, ensuring consistent reads: If thread
+ * field is nulled out or s.prev is no longer head, then
+ * some other thread(s) concurrently performed setHead in
+ * between some of our reads. We try this twice before
+ * resorting to traversal.
*/
- for (;;) {
- Node h = head;
- if (h == null) // No queue
- return null;
+ Node h, s;
+ Thread st;
+ if (((h = head) != null && (s = h.next) != null &&
+ s.prev == head && (st = s.thread) != null) ||
+ ((h = head) != null && (s = h.next) != null &&
+ s.prev == head && (st = s.thread) != null))
+ return st;
- /*
- * The first node is normally h.next. Try to get its
- * thread field, ensuring consistent reads: If thread
- * field is nulled out or s.prev is no longer head, then
- * some other thread(s) concurrently performed setHead in
- * between some of our reads, so we must reread.
- */
- Node s = h.next;
- if (s != null) {
- Thread st = s.thread;
- Node sp = s.prev;
- if (st != null && sp == head)
- return st;
- }
+ /*
+ * Head's next field might not have been set yet, or may have
+ * been unset after setHead. So we must check to see if tail
+ * is actually first node. If not, we continue on, safely
+ * traversing from tail back to head to find first,
+ * guaranteeing termination.
+ */
- /*
- * Head's next field might not have been set yet, or may
- * have been unset after setHead. So we must check to see
- * if tail is actually first node, in almost the same way
- * as above.
- */
- Node t = tail;
- if (t == h) // Empty queue
- return null;
-
- if (t != null) {
- Thread tt = t.thread;
- Node tp = t.prev;
- if (tt != null && tp == head)
- return tt;
- }
+ Node t = tail;
+ Thread firstThread = null;
+ while (t != null && t != head) {
+ Thread tt = t.thread;
+ if (tt != null)
+ firstThread = tt;
+ t = t.prev;
}
+ return firstThread;
}
/**
- * Returns true if the given thread is currently queued.
+ * Returns true if the given thread is currently queued.
*
- * <p> This implementation traverses the queue to determine
+ * <p>This implementation traverses the queue to determine
* presence of the given thread.
*
* @param thread the thread
- * @return true if the given thread in on the queue
- * @throws NullPointerException if thread null
+ * @return {@code true} if the given thread is on the queue
+ * @throws NullPointerException if the thread is null
*/
public final boolean isQueued(Thread thread) {
if (thread == null)
@@ -1298,6 +1400,77 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
return false;
}
+ /**
+ * Returns {@code true} if the apparent first queued thread, if one
+ * exists, is waiting in exclusive mode. If this method returns
+ * {@code true}, and the current thread is attempting to acquire in
+ * shared mode (that is, this method is invoked from {@link
+ * #tryAcquireShared}) then it is guaranteed that the current thread
+ * is not the first queued thread. Used only as a heuristic in
+ * ReentrantReadWriteLock.
+ */
+ final boolean apparentlyFirstQueuedIsExclusive() {
+ Node h, s;
+ return (h = head) != null &&
+ (s = h.next) != null &&
+ !s.isShared() &&
+ s.thread != null;
+ }
+
+ /**
+ * Queries whether any threads have been waiting to acquire longer
+ * than the current thread.
+ *
+ * <p>An invocation of this method is equivalent to (but may be
+ * more efficient than):
+ * <pre> {@code
+ * getFirstQueuedThread() != Thread.currentThread() &&
+ * hasQueuedThreads()}</pre>
+ *
+ * <p>Note that because cancellations due to interrupts and
+ * timeouts may occur at any time, a {@code true} return does not
+ * guarantee that some other thread will acquire before the current
+ * thread. Likewise, it is possible for another thread to win a
+ * race to enqueue after this method has returned {@code false},
+ * due to the queue being empty.
+ *
+ * <p>This method is designed to be used by a fair synchronizer to
+ * avoid <a href="AbstractQueuedSynchronizer#barging">barging</a>.
+ * Such a synchronizer's {@link #tryAcquire} method should return
+ * {@code false}, and its {@link #tryAcquireShared} method should
+ * return a negative value, if this method returns {@code true}
+ * (unless this is a reentrant acquire). For example, the {@code
+ * tryAcquire} method for a fair, reentrant, exclusive mode
+ * synchronizer might look like this:
+ *
+ * <pre> {@code
+ * protected boolean tryAcquire(int arg) {
+ * if (isHeldExclusively()) {
+ * // A reentrant acquire; increment hold count
+ * return true;
+ * } else if (hasQueuedPredecessors()) {
+ * return false;
+ * } else {
+ * // try to acquire normally
+ * }
+ * }}</pre>
+ *
+ * @return {@code true} if there is a queued thread preceding the
+ * current thread, and {@code false} if the current thread
+ * is at the head of the queue or the queue is empty
+ */
+ final boolean hasQueuedPredecessors() {
+ // The correctness of this depends on head being initialized
+ // before tail and on head.next being accurate if the current
+ // thread is first in queue.
+ Node t = tail; // Read fields in reverse initialization order
+ Node h = head;
+ Node s;
+ return h != t &&
+ ((s = h.next) == null || s.thread != Thread.currentThread());
+ }
+
+
// Instrumentation and monitoring methods
/**
@@ -1308,7 +1481,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* monitoring system state, not for synchronization
* control.
*
- * @return the estimated number of threads waiting for this lock
+ * @return the estimated number of threads waiting to acquire
*/
public final int getQueueLength() {
int n = 0;
@@ -1327,6 +1500,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* returned collection are in no particular order. This method is
* designed to facilitate construction of subclasses that provide
* more extensive monitoring facilities.
+ *
* @return the collection of threads
*/
public final Collection<Thread> getQueuedThreads() {
@@ -1344,6 +1518,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* acquire in exclusive mode. This has the same properties
* as {@link #getQueuedThreads} except that it only returns
* those threads waiting due to an exclusive acquire.
+ *
* @return the collection of threads
*/
public final Collection<Thread> getExclusiveQueuedThreads() {
@@ -1363,6 +1538,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* acquire in shared mode. This has the same properties
* as {@link #getQueuedThreads} except that it only returns
* those threads waiting due to a shared acquire.
+ *
* @return the collection of threads
*/
public final Collection<Thread> getSharedQueuedThreads() {
@@ -1378,18 +1554,18 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
}
/**
- * Returns a string identifying this synchronizer, as well as its
- * state. The state, in brackets, includes the String &quot;State
- * =&quot; followed by the current value of {@link #getState}, and
- * either &quot;nonempty&quot; or &quot;empty&quot; depending on
- * whether the queue is empty.
+ * Returns a string identifying this synchronizer, as well as its state.
+ * The state, in brackets, includes the String {@code "State ="}
+ * followed by the current value of {@link #getState}, and either
+ * {@code "nonempty"} or {@code "empty"} depending on whether the
+ * queue is empty.
*
- * @return a string identifying this synchronizer, as well as its state.
+ * @return a string identifying this synchronizer, as well as its state
*/
public String toString() {
int s = getState();
- String q = hasQueuedThreads()? "non" : "";
- return super.toString() +
+ String q = hasQueuedThreads() ? "non" : "";
+ return super.toString() +
"[State = " + s + ", " + q + "empty queue]";
}
@@ -1416,7 +1592,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* there, so we hardly ever traverse much.
*/
return findNodeFromTail(node);
- }
+ }
/**
* Returns true if node is on sync queue by searching backwards from tail.
@@ -1424,7 +1600,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* @return true if present
*/
private boolean findNodeFromTail(Node node) {
- Node t = tail;
+ Node t = tail;
for (;;) {
if (t == node)
return true;
@@ -1435,7 +1611,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
}
/**
- * Transfers a node from a condition queue onto sync queue.
+ * Transfers a node from a condition queue onto sync queue.
* Returns true if successful.
* @param node the node
* @return true if successfully transferred (else the node was
@@ -1455,8 +1631,8 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* case the waitStatus can be transiently and harmlessly wrong).
*/
Node p = enq(node);
- int c = p.waitStatus;
- if (c > 0 || !compareAndSetWaitStatus(p, c, Node.SIGNAL))
+ int ws = p.waitStatus;
+ if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
@@ -1465,9 +1641,8 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* Transfers node, if necessary, to sync queue after a cancelled
* wait. Returns true if thread was cancelled before being
* signalled.
- * @param current the waiting thread
* @param node its node
- * @return true if cancelled before the node was signalled.
+ * @return true if cancelled before the node was signalled
*/
final boolean transferAfterCancelledWait(Node node) {
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
@@ -1480,39 +1655,42 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* incomplete transfer is both rare and transient, so just
* spin.
*/
- while (!isOnSyncQueue(node))
+ while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
/**
- * Invoke release with current state value; return saved state.
- * Cancel node and throw exception on failure.
+ * Invokes release with current state value; returns saved state.
+ * Cancels node and throws exception on failure.
* @param node the condition node for this wait
* @return previous sync state
*/
final int fullyRelease(Node node) {
+ boolean failed = true;
try {
int savedState = getState();
- if (release(savedState))
+ if (release(savedState)) {
+ failed = false;
return savedState;
- } catch(RuntimeException ex) {
- node.waitStatus = Node.CANCELLED;
- throw ex;
+ } else {
+ throw new IllegalMonitorStateException();
+ }
+ } finally {
+ if (failed)
+ node.waitStatus = Node.CANCELLED;
}
- // reach here if release fails
- node.waitStatus = Node.CANCELLED;
- throw new IllegalMonitorStateException();
}
// Instrumentation methods for conditions
/**
- * Queries whether the given ConditionObject
+ * Queries whether the given ConditionObject
* uses this synchronizer as its lock.
+ *
* @param condition the condition
* @return <tt>true</tt> if owned
- * @throws NullPointerException if condition null
+ * @throws NullPointerException if the condition is null
*/
public final boolean owns(ConditionObject condition) {
if (condition == null)
@@ -1527,14 +1705,15 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* does not guarantee that a future <tt>signal</tt> will awaken
* any threads. This method is designed primarily for use in
* monitoring of the system state.
+ *
* @param condition the condition
- * @return <tt>true</tt> if there are any waiting threads.
- * @throws IllegalMonitorStateException if exclusive synchronization
- * is not held
+ * @return <tt>true</tt> if there are any waiting threads
+ * @throws IllegalMonitorStateException if exclusive synchronization
+ * is not held
* @throws IllegalArgumentException if the given condition is
- * not associated with this synchronizer
- * @throws NullPointerException if condition null
- */
+ * not associated with this synchronizer
+ * @throws NullPointerException if the condition is null
+ */
public final boolean hasWaiters(ConditionObject condition) {
if (!owns(condition))
throw new IllegalArgumentException("Not owner");
@@ -1548,14 +1727,15 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* estimate serves only as an upper bound on the actual number of
* waiters. This method is designed for use in monitoring of the
* system state, not for synchronization control.
+ *
* @param condition the condition
- * @return the estimated number of waiting threads.
- * @throws IllegalMonitorStateException if exclusive synchronization
- * is not held
+ * @return the estimated number of waiting threads
+ * @throws IllegalMonitorStateException if exclusive synchronization
+ * is not held
* @throws IllegalArgumentException if the given condition is
- * not associated with this synchronizer
- * @throws NullPointerException if condition null
- */
+ * not associated with this synchronizer
+ * @throws NullPointerException if the condition is null
+ */
public final int getWaitQueueLength(ConditionObject condition) {
if (!owns(condition))
throw new IllegalArgumentException("Not owner");
@@ -1568,14 +1748,15 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* synchronizer. Because the actual set of threads may change
* dynamically while constructing this result, the returned
* collection is only a best-effort estimate. The elements of the
- * returned collection are in no particular order.
+ * returned collection are in no particular order.
+ *
* @param condition the condition
* @return the collection of threads
- * @throws IllegalMonitorStateException if exclusive synchronization
- * is not held
+ * @throws IllegalMonitorStateException if exclusive synchronization
+ * is not held
* @throws IllegalArgumentException if the given condition is
- * not associated with this synchronizer
- * @throws NullPointerException if condition null
+ * not associated with this synchronizer
+ * @throws NullPointerException if the condition is null
*/
public final Collection<Thread> getWaitingThreads(ConditionObject condition) {
if (!owns(condition))
@@ -1588,14 +1769,14 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* AbstractQueuedSynchronizer} serving as the basis of a {@link
* Lock} implementation.
*
- * <p> Method documentation for this class describes mechanics,
+ * <p>Method documentation for this class describes mechanics,
* not behavioral specifications from the point of view of Lock
* and Condition users. Exported versions of this class will in
* general need to be accompanied by documentation describing
* condition semantics that rely on those of the associated
* <tt>AbstractQueuedSynchronizer</tt>.
- *
- * <p> This class is Serializable, but all fields are transient,
+ *
+ * <p>This class is Serializable, but all fields are transient,
* so deserialized conditions have no waiters.
*/
public class ConditionObject implements Condition, java.io.Serializable {
@@ -1613,29 +1794,34 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
// Internal methods
/**
- * Add a new waiter to wait queue
+ * Adds a new waiter to wait queue.
* @return its new wait node
*/
private Node addConditionWaiter() {
- Node node = new Node(Thread.currentThread(), Node.CONDITION);
Node t = lastWaiter;
- if (t == null)
+ // If lastWaiter is cancelled, clean out.
+ if (t != null && t.waitStatus != Node.CONDITION) {
+ unlinkCancelledWaiters();
+ t = lastWaiter;
+ }
+ Node node = new Node(Thread.currentThread(), Node.CONDITION);
+ if (t == null)
firstWaiter = node;
- else
+ else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
/**
- * Remove and transfer nodes until hit non-cancelled one or
+ * Removes and transfers nodes until hit non-cancelled one or
* null. Split out from signal in part to encourage compilers
* to inline the case of no waiters.
* @param first (non-null) the first node on condition queue
*/
private void doSignal(Node first) {
do {
- if ( (firstWaiter = first.nextWaiter) == null)
+ if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
@@ -1643,11 +1829,11 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
}
/**
- * Remove and transfer all nodes.
+ * Removes and transfers all nodes.
* @param first (non-null) the first node on condition queue
*/
private void doSignalAll(Node first) {
- lastWaiter = firstWaiter = null;
+ lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
@@ -1656,45 +1842,81 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
} while (first != null);
}
+ /**
+ * Unlinks cancelled waiter nodes from condition queue.
+ * Called only while holding lock. This is called when
+ * cancellation occurred during condition wait, and upon
+ * insertion of a new waiter when lastWaiter is seen to have
+ * been cancelled. This method is needed to avoid garbage
+ * retention in the absence of signals. So even though it may
+ * require a full traversal, it comes into play only when
+ * timeouts or cancellations occur in the absence of
+ * signals. It traverses all nodes rather than stopping at a
+ * particular target to unlink all pointers to garbage nodes
+ * without requiring many re-traversals during cancellation
+ * storms.
+ */
+ private void unlinkCancelledWaiters() {
+ Node t = firstWaiter;
+ Node trail = null;
+ while (t != null) {
+ Node next = t.nextWaiter;
+ if (t.waitStatus != Node.CONDITION) {
+ t.nextWaiter = null;
+ if (trail == null)
+ firstWaiter = next;
+ else
+ trail.nextWaiter = next;
+ if (next == null)
+ lastWaiter = trail;
+ }
+ else
+ trail = t;
+ t = next;
+ }
+ }
+
// public methods
/**
* Moves the longest-waiting thread, if one exists, from the
* wait queue for this condition to the wait queue for the
* owning lock.
+ *
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
- * returns false
+ * returns {@code false}
*/
public final void signal() {
- if (!isHeldExclusively())
+ if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
-
+
/**
* Moves all threads from the wait queue for this condition to
* the wait queue for the owning lock.
+ *
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
- * returns false
+ * returns {@code false}
*/
public final void signalAll() {
- if (!isHeldExclusively())
+ if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
- if (first != null)
+ if (first != null)
doSignalAll(first);
}
/**
* Implements uninterruptible condition wait.
* <ol>
- * <li> Save lock state returned by {@link #getState}
- * <li> Invoke {@link #release} with
- * saved state as argument, throwing
- * IllegalMonitorStateException if it fails.
- * <li> Block until signalled
+ * <li> Save lock state returned by {@link #getState}.
+ * <li> Invoke {@link #release} with
+ * saved state as argument, throwing
+ * IllegalMonitorStateException if it fails.
+ * <li> Block until signalled.
* <li> Reacquire by invoking specialized version of
* {@link #acquire} with saved state as argument.
* </ol>
@@ -1705,7 +1927,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
boolean interrupted = false;
while (!isOnSyncQueue(node)) {
LockSupport.park();
- if (Thread.interrupted())
+ if (Thread.interrupted())
interrupted = true;
}
if (acquireQueued(node, savedState) || interrupted)
@@ -1725,47 +1947,47 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
private static final int THROW_IE = -1;
/**
- * Check for interrupt, returning THROW_IE if interrupted
+ * Checks for interrupt, returning THROW_IE if interrupted
* before signalled, REINTERRUPT if after signalled, or
* 0 if not interrupted.
*/
private int checkInterruptWhileWaiting(Node node) {
- return (Thread.interrupted()) ?
- ((transferAfterCancelledWait(node))? THROW_IE : REINTERRUPT) :
+ return Thread.interrupted() ?
+ (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
/**
- * Throw InterruptedException, reinterrupt current thread, or
- * do nothing, depending on mode.
+ * Throws InterruptedException, reinterrupts current thread, or
+ * does nothing, depending on mode.
*/
- private void reportInterruptAfterWait(int interruptMode)
+ private void reportInterruptAfterWait(int interruptMode)
throws InterruptedException {
if (interruptMode == THROW_IE)
throw new InterruptedException();
else if (interruptMode == REINTERRUPT)
- Thread.currentThread().interrupt();
+ selfInterrupt();
}
/**
* Implements interruptible condition wait.
* <ol>
- * <li> If current thread is interrupted, throw InterruptedException
- * <li> Save lock state returned by {@link #getState}
- * <li> Invoke {@link #release} with
- * saved state as argument, throwing
- * IllegalMonitorStateException if it fails.
- * <li> Block until signalled or interrupted
+ * <li> If current thread is interrupted, throw InterruptedException.
+ * <li> Save lock state returned by {@link #getState}.
+ * <li> Invoke {@link #release} with
+ * saved state as argument, throwing
+ * IllegalMonitorStateException if it fails.
+ * <li> Block until signalled or interrupted.
* <li> Reacquire by invoking specialized version of
* {@link #acquire} with saved state as argument.
- * <li> If interrupted while blocked in step 4, throw exception
+ * <li> If interrupted while blocked in step 4, throw InterruptedException.
* </ol>
- *
+ *
* @throws InterruptedException if the current thread is interrupted (and
* interruption of thread suspension is supported).
*/
public final void await() throws InterruptedException {
- if (Thread.interrupted())
+ if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
@@ -1777,6 +1999,8 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
+ if (node.nextWaiter != null) // clean up if cancelled
+ unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
@@ -1784,17 +2008,17 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
/**
* Implements timed condition wait.
* <ol>
- * <li> If current thread is interrupted, throw InterruptedException
- * <li> Save lock state returned by {@link #getState}
- * <li> Invoke {@link #release} with
- * saved state as argument, throwing
- * IllegalMonitorStateException if it fails.
- * <li> Block until signalled, interrupted, or timed out
+ * <li> If current thread is interrupted, throw InterruptedException.
+ * <li> Save lock state returned by {@link #getState}.
+ * <li> Invoke {@link #release} with
+ * saved state as argument, throwing
+ * IllegalMonitorStateException if it fails.
+ * <li> Block until signalled, interrupted, or timed out.
* <li> Reacquire by invoking specialized version of
* {@link #acquire} with saved state as argument.
- * <li> If interrupted while blocked in step 4, throw InterruptedException
+ * <li> If interrupted while blocked in step 4, throw InterruptedException.
* </ol>
- *
+ *
* @param nanosTimeout the maximum time to wait, in nanoseconds
* @return A value less than or equal to zero if the wait has
* timed out; otherwise an estimate, that
@@ -1805,7 +2029,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
* interruption of thread suspension is supported).
*/
public final long awaitNanos(long nanosTimeout) throws InterruptedException {
- if (Thread.interrupted())
+ if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
@@ -1813,18 +2037,21 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
- transferAfterCancelledWait(node);
+ transferAfterCancelledWait(node);
break;
}
LockSupport.parkNanos(nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
+
long now = System.nanoTime();
nanosTimeout -= now - lastTime;
lastTime = now;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
+ if (node.nextWaiter != null)
+ unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return nanosTimeout - (System.nanoTime() - lastTime);
@@ -1833,18 +2060,18 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
/**
* Implements absolute timed condition wait.
* <ol>
- * <li> If current thread is interrupted, throw InterruptedException
- * <li> Save lock state returned by {@link #getState}
- * <li> Invoke {@link #release} with
- * saved state as argument, throwing
- * IllegalMonitorStateException if it fails.
- * <li> Block until signalled, interrupted, or timed out
+ * <li> If current thread is interrupted, throw InterruptedException.
+ * <li> Save lock state returned by {@link #getState}.
+ * <li> Invoke {@link #release} with
+ * saved state as argument, throwing
+ * IllegalMonitorStateException if it fails.
+ * <li> Block until signalled, interrupted, or timed out.
* <li> Reacquire by invoking specialized version of
* {@link #acquire} with saved state as argument.
- * <li> If interrupted while blocked in step 4, throw InterruptedException
- * <li> If timed out while blocked in step 4, return false, else true
+ * <li> If interrupted while blocked in step 4, throw InterruptedException.
+ * <li> If timed out while blocked in step 4, return false, else true.
* </ol>
- *
+ *
* @param deadline the absolute time to wait until
* @return <tt>false</tt> if the deadline has
* elapsed upon return, else <tt>true</tt>.
@@ -1856,7 +2083,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
if (deadline == null)
throw new NullPointerException();
long abstime = deadline.getTime();
- if (Thread.interrupted())
+ if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
@@ -1864,7 +2091,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (System.currentTimeMillis() > abstime) {
- timedout = transferAfterCancelledWait(node);
+ timedout = transferAfterCancelledWait(node);
break;
}
LockSupport.parkUntil(abstime);
@@ -1873,26 +2100,28 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
+ if (node.nextWaiter != null)
+ unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return !timedout;
}
-
+
/**
- * Implements timed condition wait.
+ * Implements timed condition wait.
* <ol>
- * <li> If current thread is interrupted, throw InterruptedException
- * <li> Save lock state returned by {@link #getState}
- * <li> Invoke {@link #release} with
- * saved state as argument, throwing
- * IllegalMonitorStateException if it fails.
- * <li> Block until signalled, interrupted, or timed out
+ * <li> If current thread is interrupted, throw InterruptedException.
+ * <li> Save lock state returned by {@link #getState}.
+ * <li> Invoke {@link #release} with
+ * saved state as argument, throwing
+ * IllegalMonitorStateException if it fails.
+ * <li> Block until signalled, interrupted, or timed out.
* <li> Reacquire by invoking specialized version of
* {@link #acquire} with saved state as argument.
- * <li> If interrupted while blocked in step 4, throw InterruptedException
- * <li> If timed out while blocked in step 4, return false, else true
+ * <li> If interrupted while blocked in step 4, throw InterruptedException.
+ * <li> If timed out while blocked in step 4, return false, else true.
* </ol>
- *
+ *
* @param time the maximum time to wait
* @param unit the time unit of the <tt>time</tt> argument.
* @return <tt>false</tt> if the waiting time detectably elapsed
@@ -1904,7 +2133,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
if (unit == null)
throw new NullPointerException();
long nanosTimeout = unit.toNanos(time);
- if (Thread.interrupted())
+ if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
@@ -1913,10 +2142,11 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
- timedout = transferAfterCancelledWait(node);
+ timedout = transferAfterCancelledWait(node);
break;
}
- LockSupport.parkNanos(nanosTimeout);
+ if (nanosTimeout >= spinForTimeoutThreshold)
+ LockSupport.parkNanos(nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
long now = System.nanoTime();
@@ -1925,6 +2155,8 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
+ if (node.nextWaiter != null)
+ unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return !timedout;
@@ -1934,8 +2166,9 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
/**
* Returns true if this condition was created by the given
- * synchronization object
- * @return true if owned
+ * synchronization object.
+ *
+ * @return {@code true} if owned
*/
final boolean isOwnedBy(AbstractQueuedSynchronizer sync) {
return sync == AbstractQueuedSynchronizer.this;
@@ -1943,13 +2176,14 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
/**
* Queries whether any threads are waiting on this condition.
- * Implements {@link AbstractQueuedSynchronizer#hasWaiters}
- * @return <tt>true</tt> if there are any waiting threads.
+ * Implements {@link AbstractQueuedSynchronizer#hasWaiters}.
+ *
+ * @return {@code true} if there are any waiting threads
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
- * returns false
- */
+ * returns {@code false}
+ */
protected final boolean hasWaiters() {
- if (!isHeldExclusively())
+ if (!isHeldExclusively())
throw new IllegalMonitorStateException();
for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
if (w.waitStatus == Node.CONDITION)
@@ -1960,14 +2194,15 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
/**
* Returns an estimate of the number of threads waiting on
- * this condition.
- * Implements {@link AbstractQueuedSynchronizer#getWaitQueueLength}
- * @return the estimated number of waiting threads.
+ * this condition.
+ * Implements {@link AbstractQueuedSynchronizer#getWaitQueueLength}.
+ *
+ * @return the estimated number of waiting threads
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
- * returns false
- */
+ * returns {@code false}
+ */
protected final int getWaitQueueLength() {
- if (!isHeldExclusively())
+ if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int n = 0;
for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
@@ -1979,14 +2214,15 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
/**
* Returns a collection containing those threads that may be
- * waiting on this Condition.
- * Implements {@link AbstractQueuedSynchronizer#getWaitingThreads}
+ * waiting on this Condition.
+ * Implements {@link AbstractQueuedSynchronizer#getWaitingThreads}.
+ *
* @return the collection of threads
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
- * returns false
+ * returns {@code false}
*/
protected final Collection<Thread> getWaitingThreads() {
- if (!isHeldExclusively())
+ if (!isHeldExclusively())
throw new IllegalMonitorStateException();
ArrayList<Thread> list = new ArrayList<Thread>();
for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
@@ -2016,6 +2252,7 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
+ private static final long nextOffset;
static {
try {
@@ -2027,19 +2264,21 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
-
- } catch(Exception ex) { throw new Error(ex); }
+ nextOffset = unsafe.objectFieldOffset
+ (Node.class.getDeclaredField("next"));
+
+ } catch (Exception ex) { throw new Error(ex); }
}
/**
- * CAS head field. Used only by enq
+ * CAS head field. Used only by enq.
*/
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
-
+
/**
- * CAS tail field. Used only by enq
+ * CAS tail field. Used only by enq.
*/
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
@@ -2048,11 +2287,19 @@ public abstract class AbstractQueuedSynchronizer implements java.io.Serializable
/**
* CAS waitStatus field of a node.
*/
- private final static boolean compareAndSetWaitStatus(Node node,
- int expect,
+ private final static boolean compareAndSetWaitStatus(Node node,
+ int expect,
int update) {
- return unsafe.compareAndSwapInt(node, waitStatusOffset,
+ return unsafe.compareAndSwapInt(node, waitStatusOffset,
expect, update);
}
+ /**
+ * CAS next field of a node.
+ */
+ private final static boolean compareAndSetNext(Node node,
+ Node expect,
+ Node update) {
+ return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
+ }
}
diff --git a/concurrent/src/main/java/java/util/concurrent/locks/Condition.java b/concurrent/src/main/java/java/util/concurrent/locks/Condition.java
index 7a8ca5b..03be58f 100644
--- a/concurrent/src/main/java/java/util/concurrent/locks/Condition.java
+++ b/concurrent/src/main/java/java/util/concurrent/locks/Condition.java
@@ -9,13 +9,13 @@ import java.util.concurrent.*;
import java.util.Date;
/**
- * <tt>Condition</tt> factors out the <tt>Object</tt> monitor
+ * {@code Condition} factors out the {@code Object} monitor
* methods ({@link Object#wait() wait}, {@link Object#notify notify}
* and {@link Object#notifyAll notifyAll}) into distinct objects to
* give the effect of having multiple wait-sets per object, by
* combining them with the use of arbitrary {@link Lock} implementations.
- * Where a <tt>Lock</tt> replaces the use of <tt>synchronized</tt> methods
- * and statements, a <tt>Condition</tt> replaces the use of the Object
+ * Where a {@code Lock} replaces the use of {@code synchronized} methods
+ * and statements, a {@code Condition} replaces the use of the Object
* monitor methods.
*
* <p>Conditions (also known as <em>condition queues</em> or
@@ -26,37 +26,37 @@ import java.util.Date;
* 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 it <em>atomically</em> releases the associated lock and
- * suspends the current thread, just like <tt>Object.wait</tt>.
+ * suspends the current thread, just like {@code Object.wait}.
*
- * <p>A <tt>Condition</tt> instance is intrinsically bound to a lock.
- * To obtain a <tt>Condition</tt> instance for a particular {@link Lock}
+ * <p>A {@code Condition} instance is intrinsically bound to a lock.
+ * To obtain a {@code Condition} instance for a particular {@link Lock}
* instance use its {@link Lock#newCondition newCondition()} method.
*
* <p>As an example, suppose we have a bounded buffer which supports
- * <tt>put</tt> and <tt>take</tt> methods. If a
- * <tt>take</tt> is attempted on an empty buffer, then the thread will block
- * until an item becomes available; if a <tt>put</tt> is attempted on a
+ * {@code put} and {@code take} methods. If a
+ * {@code take} is attempted on an empty buffer, then the thread will block
+ * until an item becomes available; if a {@code put} is attempted on a
* full buffer, then the thread will block until a space becomes available.
- * We would like to keep waiting <tt>put</tt> threads and <tt>take</tt>
+ * We would like to keep waiting {@code put} threads and {@code take}
* threads in separate wait-sets so that we can use the optimization of
* only notifying a single thread at a time when items or spaces become
- * available in the buffer. This can be achieved using two
+ * available in the buffer. This can be achieved using two
* {@link Condition} instances.
* <pre>
* class BoundedBuffer {
- * <b>Lock lock = new ReentrantLock();</b>
+ * <b>final Lock lock = new ReentrantLock();</b>
* final Condition notFull = <b>lock.newCondition(); </b>
* final Condition notEmpty = <b>lock.newCondition(); </b>
*
- * Object[] items = new Object[100];
+ * final Object[] items = new Object[100];
* int putptr, takeptr, count;
*
* public void put(Object x) throws InterruptedException {
* <b>lock.lock();
* try {</b>
- * while (count == items.length)
+ * while (count == items.length)
* <b>notFull.await();</b>
- * items[putptr] = x;
+ * items[putptr] = x;
* if (++putptr == items.length) putptr = 0;
* ++count;
* <b>notEmpty.signal();</b>
@@ -68,9 +68,9 @@ import java.util.Date;
* public Object take() throws InterruptedException {
* <b>lock.lock();
* try {</b>
- * while (count == 0)
+ * while (count == 0)
* <b>notEmpty.await();</b>
- * Object x = items[takeptr];
+ * Object x = items[takeptr];
* if (++takeptr == items.length) takeptr = 0;
* --count;
* <b>notFull.signal();</b>
@@ -78,7 +78,7 @@ import java.util.Date;
* <b>} finally {
* lock.unlock();
* }</b>
- * }
+ * }
* }
* </pre>
*
@@ -86,61 +86,63 @@ import java.util.Date;
* this functionality, so there is no reason to implement this
* sample usage class.)
*
- * <p>A <tt>Condition</tt> implementation can provide behavior and semantics
- * that is
- * different from that of the <tt>Object</tt> monitor methods, such as
- * guaranteed ordering for notifications, or not requiring a lock to be held
+ * <p>A {@code Condition} implementation can provide behavior and semantics
+ * that is
+ * different from that of the {@code Object} monitor methods, such as
+ * guaranteed ordering for notifications, or not requiring a lock to be held
* when performing notifications.
- * If an implementation provides such specialized semantics then the
+ * If an implementation provides such specialized semantics then the
* implementation must document those semantics.
*
- * <p>Note that <tt>Condition</tt> instances are just normal objects and can
- * themselves be used as the target in a <tt>synchronized</tt> statement,
+ * <p>Note that {@code Condition} instances are just normal objects and can
+ * themselves be used as the target in a {@code synchronized} statement,
* and can have their own monitor {@link Object#wait wait} and
* {@link Object#notify notification} methods invoked.
- * Acquiring the monitor lock of a <tt>Condition</tt> instance, or using its
+ * Acquiring the monitor lock of a {@code Condition} instance, or using its
* monitor methods, has no specified relationship with acquiring the
- * {@link Lock} associated with that <tt>Condition</tt> or the use of its
- * {@link #await waiting} and {@link #signal signalling} methods.
- * It is recommended that to avoid confusion you never use <tt>Condition</tt>
+ * {@link Lock} associated with that {@code Condition} or the use of its
+ * {@linkplain #await waiting} and {@linkplain #signal signalling} methods.
+ * It is recommended that to avoid confusion you never use {@code Condition}
* instances in this way, except perhaps within their own implementation.
*
- * <p>Except where noted, passing a <tt>null</tt> value for any parameter
+ * <p>Except where noted, passing a {@code null} value for any parameter
* will result in a {@link NullPointerException} being thrown.
*
* <h3>Implementation Considerations</h3>
*
- * <p>When waiting upon a <tt>Condition</tt>, a &quot;<em>spurious
- * wakeup</em>&quot; is permitted to occur, in
+ * <p>When waiting upon a {@code Condition}, a &quot;<em>spurious
+ * wakeup</em>&quot; is permitted to occur, in
* general, as a concession to the underlying platform semantics.
* This has little practical impact on most application programs as a
- * <tt>Condition</tt> should always be waited upon in a loop, testing
+ * {@code Condition} should always be waited upon in a loop, testing
* the state predicate that is being waited for. An implementation is
- * free to remove the possibility of spurious wakeups but it is
+ * free to remove the possibility of spurious wakeups but it is
* recommended that applications programmers always assume that they can
* occur and so always wait in a loop.
*
- * <p>The three forms of condition waiting
- * (interruptible, non-interruptible, and timed) may differ in their ease of
+ * <p>The three forms of condition waiting
+ * (interruptible, non-interruptible, and timed) may differ in their ease of
* implementation on some platforms and in their performance characteristics.
- * In particular, it may be difficult to provide these features and maintain
- * specific semantics such as ordering guarantees.
- * Further, the ability to interrupt the actual suspension of the thread may
+ * In particular, it may be difficult to provide these features and maintain
+ * specific semantics such as ordering guarantees.
+ * Further, the ability to interrupt the actual suspension of the thread may
* not always be feasible to implement on all platforms.
- * <p>Consequently, an implementation is not required to define exactly the
- * same guarantees or semantics for all three forms of waiting, nor is it
+ *
+ * <p>Consequently, an implementation is not required to define exactly the
+ * same guarantees or semantics for all three forms of waiting, nor is it
* required to support interruption of the actual suspension of the thread.
+ *
* <p>An implementation is required to
- * clearly document the semantics and guarantees provided by each of the
- * waiting methods, and when an implementation does support interruption of
- * thread suspension then it must obey the interruption semantics as defined
+ * clearly document the semantics and guarantees provided by each of the
+ * waiting methods, and when an implementation does support interruption of
+ * thread suspension then it must obey the interruption semantics as defined
* in this interface.
- * <p>As interruption generally implies cancellation, and checks for
+ *
+ * <p>As interruption generally implies cancellation, and checks for
* interruption are often infrequent, an implementation can favor responding
* to an interrupt over normal method return. This is true even if it can be
- * shown that the interrupt occurred after another action may have unblocked
- * the thread. An implementation should document this behavior.
- *
+ * shown that the interrupt occurred after another action that may have
+ * unblocked the thread. An implementation should document this behavior.
*
* @since 1.5
* @author Doug Lea
@@ -148,21 +150,21 @@ import java.util.Date;
public interface Condition {
/**
- * Causes the current thread to wait until it is signalled or
- * {@link Thread#interrupt interrupted}.
+ * Causes the current thread to wait until it is signalled or
+ * {@linkplain Thread#interrupt interrupted}.
*
- * <p>The lock associated with this <tt>Condition</tt> is atomically
- * released and the current thread becomes disabled for thread scheduling
+ * <p>The lock associated with this {@code Condition} is atomically
+ * released and the current thread becomes disabled for thread scheduling
* purposes and lies dormant until <em>one</em> of four things happens:
* <ul>
- * <li>Some other thread invokes the {@link #signal} method for this
- * <tt>Condition</tt> and the current thread happens to be chosen as the
+ * <li>Some other thread invokes the {@link #signal} method for this
+ * {@code Condition} and the current thread happens to be chosen as the
* thread to be awakened; or
- * <li>Some other thread invokes the {@link #signalAll} method for this
- * <tt>Condition</tt>; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread, and interruption of thread suspension is supported; or
- * <li>A &quot;<em>spurious wakeup</em>&quot; occurs
+ * <li>Some other thread invokes the {@link #signalAll} method for this
+ * {@code Condition}; or
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
+ * current thread, and interruption of thread suspension is supported; or
+ * <li>A &quot;<em>spurious wakeup</em>&quot; occurs.
* </ul>
*
* <p>In all cases, before this method can return the current thread must
@@ -171,20 +173,21 @@ public interface Condition {
*
* <p>If the current thread:
* <ul>
- * <li>has its interrupted status set on entry to this method; or
- * <li>is {@link Thread#interrupt interrupted} while waiting
- * and interruption of thread suspension is supported,
+ * <li>has its interrupted status set on entry to this method; or
+ * <li>is {@linkplain Thread#interrupt interrupted} while waiting
+ * and interruption of thread suspension is supported,
* </ul>
- * then {@link InterruptedException} is thrown and the current thread's
+ * then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared. It is not specified, in the first
* case, whether or not the test for interruption occurs before the lock
* is released.
- *
+ *
* <p><b>Implementation Considerations</b>
+ *
* <p>The current thread is assumed to hold the lock associated with this
- * <tt>Condition</tt> when this method is called.
+ * {@code Condition} when this method is called.
* It is up to the implementation to determine if this is
- * the case and if not, how to respond. Typically, an exception will be
+ * the case and if not, how to respond. Typically, an exception will be
* thrown (such as {@link IllegalMonitorStateException}) and the
* implementation must document that fact.
*
@@ -193,62 +196,62 @@ public interface Condition {
* must ensure that the signal is redirected to another waiting thread, if
* there is one.
*
- * @throws InterruptedException if the current thread is interrupted (and
- * interruption of thread suspension is supported).
- **/
+ * @throws InterruptedException if the current thread is interrupted
+ * (and interruption of thread suspension is supported)
+ */
void await() throws InterruptedException;
/**
* Causes the current thread to wait until it is signalled.
*
- * <p>The lock associated with this condition is atomically
- * released and the current thread becomes disabled for thread scheduling
+ * <p>The lock associated with this condition is atomically
+ * released and the current thread becomes disabled for thread scheduling
* purposes and lies dormant until <em>one</em> of three things happens:
* <ul>
- * <li>Some other thread invokes the {@link #signal} method for this
- * <tt>Condition</tt> and the current thread happens to be chosen as the
+ * <li>Some other thread invokes the {@link #signal} method for this
+ * {@code Condition} and the current thread happens to be chosen as the
* thread to be awakened; or
- * <li>Some other thread invokes the {@link #signalAll} method for this
- * <tt>Condition</tt>; or
- * <li>A &quot;<em>spurious wakeup</em>&quot; occurs
+ * <li>Some other thread invokes the {@link #signalAll} method for this
+ * {@code Condition}; or
+ * <li>A &quot;<em>spurious wakeup</em>&quot; occurs.
* </ul>
*
* <p>In all cases, before this method can return the current thread must
* re-acquire the lock associated with this condition. When the
* thread returns it is <em>guaranteed</em> to hold this lock.
*
- * <p>If the current thread's interrupt status is set when it enters
- * this method, or it is {@link Thread#interrupt interrupted}
+ * <p>If the current thread's interrupted status is set when it enters
+ * this method, or it is {@linkplain Thread#interrupt interrupted}
* while waiting, it will continue to wait until signalled. When it finally
- * returns from this method its <em>interrupted status</em> will still
+ * returns from this method its interrupted status will still
* be set.
- *
+ *
* <p><b>Implementation Considerations</b>
+ *
* <p>The current thread is assumed to hold the lock associated with this
- * <tt>Condition</tt> when this method is called.
+ * {@code Condition} when this method is called.
* It is up to the implementation to determine if this is
- * the case and if not, how to respond. Typically, an exception will be
+ * the case and if not, how to respond. Typically, an exception will be
* thrown (such as {@link IllegalMonitorStateException}) and the
* implementation must document that fact.
- *
- **/
+ */
void awaitUninterruptibly();
/**
* Causes the current thread to wait until it is signalled or interrupted,
* or the specified waiting time elapses.
*
- * <p>The lock associated with this condition is atomically
- * released and the current thread becomes disabled for thread scheduling
+ * <p>The lock associated with this condition is atomically
+ * released and the current thread becomes disabled for thread scheduling
* purposes and lies dormant until <em>one</em> of five things happens:
* <ul>
- * <li>Some other thread invokes the {@link #signal} method for this
- * <tt>Condition</tt> and the current thread happens to be chosen as the
- * thread to be awakened; or
- * <li>Some other thread invokes the {@link #signalAll} method for this
- * <tt>Condition</tt>; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread, and interruption of thread suspension is supported; or
+ * <li>Some other thread invokes the {@link #signal} method for this
+ * {@code Condition} and the current thread happens to be chosen as the
+ * thread to be awakened; or
+ * <li>Some other thread invokes the {@link #signalAll} method for this
+ * {@code Condition}; or
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
+ * current thread, and interruption of thread suspension is supported; or
* <li>The specified waiting time elapses; or
* <li>A &quot;<em>spurious wakeup</em>&quot; occurs.
* </ul>
@@ -259,17 +262,17 @@ public interface Condition {
*
* <p>If the current thread:
* <ul>
- * <li>has its interrupted status set on entry to this method; or
- * <li>is {@link Thread#interrupt interrupted} while waiting
- * and interruption of thread suspension is supported,
+ * <li>has its interrupted status set on entry to this method; or
+ * <li>is {@linkplain Thread#interrupt interrupted} while waiting
+ * and interruption of thread suspension is supported,
* </ul>
- * then {@link InterruptedException} is thrown and the current thread's
+ * then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared. It is not specified, in the first
* case, whether or not the test for interruption occurs before the lock
* is released.
*
* <p>The method returns an estimate of the number of nanoseconds
- * remaining to wait given the supplied <tt>nanosTimeout</tt>
+ * remaining to wait given the supplied {@code nanosTimeout}
* value upon return, or a value less than or equal to zero if it
* timed out. This value can be used to determine whether and how
* long to re-wait in cases where the wait returns but an awaited
@@ -285,7 +288,7 @@ public interface Condition {
* else
* return false;
* }
- * // ...
+ * // ...
* }
* </pre>
*
@@ -296,10 +299,11 @@ public interface Condition {
* than specified when re-waits occur.
*
* <p><b>Implementation Considerations</b>
+ *
* <p>The current thread is assumed to hold the lock associated with this
- * <tt>Condition</tt> when this method is called.
+ * {@code Condition} when this method is called.
* It is up to the implementation to determine if this is
- * the case and if not, how to respond. Typically, an exception will be
+ * the case and if not, how to respond. Typically, an exception will be
* thrown (such as {@link IllegalMonitorStateException}) and the
* implementation must document that fact.
*
@@ -310,13 +314,14 @@ public interface Condition {
* there is one.
*
* @param nanosTimeout the maximum time to wait, in nanoseconds
- * @return A value less than or equal to zero if the wait has
- * timed out; otherwise an estimate, that
- * is strictly less than the <tt>nanosTimeout</tt> argument,
- * of the time still remaining when this method returned.
- *
- * @throws InterruptedException if the current thread is interrupted (and
- * interruption of thread suspension is supported).
+ * @return an estimate of the {@code nanosTimeout} value minus
+ * the time spent waiting upon return from this method.
+ * A positive value may be used as the argument to a
+ * subsequent call to this method to finish waiting out
+ * the desired time. A value less than or equal to zero
+ * indicates that no time remains.
+ * @throws InterruptedException if the current thread is interrupted
+ * (and interruption of thread suspension is supported)
*/
long awaitNanos(long nanosTimeout) throws InterruptedException;
@@ -328,29 +333,29 @@ public interface Condition {
* awaitNanos(unit.toNanos(time)) &gt; 0
* </pre>
* @param time the maximum time to wait
- * @param unit the time unit of the <tt>time</tt> argument.
- * @return <tt>false</tt> if the waiting time detectably elapsed
- * before return from the method, else <tt>true</tt>.
- * @throws InterruptedException if the current thread is interrupted (and
- * interruption of thread suspension is supported).
+ * @param unit the time unit of the {@code time} argument
+ * @return {@code false} if the waiting time detectably elapsed
+ * before return from the method, else {@code true}
+ * @throws InterruptedException if the current thread is interrupted
+ * (and interruption of thread suspension is supported)
*/
boolean await(long time, TimeUnit unit) throws InterruptedException;
-
+
/**
* Causes the current thread to wait until it is signalled or interrupted,
* or the specified deadline elapses.
*
- * <p>The lock associated with this condition is atomically
- * released and the current thread becomes disabled for thread scheduling
+ * <p>The lock associated with this condition is atomically
+ * released and the current thread becomes disabled for thread scheduling
* purposes and lies dormant until <em>one</em> of five things happens:
* <ul>
- * <li>Some other thread invokes the {@link #signal} method for this
- * <tt>Condition</tt> and the current thread happens to be chosen as the
- * thread to be awakened; or
- * <li>Some other thread invokes the {@link #signalAll} method for this
- * <tt>Condition</tt>; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread, and interruption of thread suspension is supported; or
+ * <li>Some other thread invokes the {@link #signal} method for this
+ * {@code Condition} and the current thread happens to be chosen as the
+ * thread to be awakened; or
+ * <li>Some other thread invokes the {@link #signalAll} method for this
+ * {@code Condition}; or
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
+ * current thread, and interruption of thread suspension is supported; or
* <li>The specified deadline elapses; or
* <li>A &quot;<em>spurious wakeup</em>&quot; occurs.
* </ul>
@@ -362,11 +367,11 @@ public interface Condition {
*
* <p>If the current thread:
* <ul>
- * <li>has its interrupted status set on entry to this method; or
- * <li>is {@link Thread#interrupt interrupted} while waiting
- * and interruption of thread suspension is supported,
+ * <li>has its interrupted status set on entry to this method; or
+ * <li>is {@linkplain Thread#interrupt interrupted} while waiting
+ * and interruption of thread suspension is supported,
* </ul>
- * then {@link InterruptedException} is thrown and the current thread's
+ * then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared. It is not specified, in the first
* case, whether or not the test for interruption occurs before the lock
* is released.
@@ -378,20 +383,21 @@ public interface Condition {
* synchronized boolean aMethod(Date deadline) {
* boolean stillWaiting = true;
* while (!conditionBeingWaitedFor) {
- * if (stillwaiting)
+ * if (stillWaiting)
* stillWaiting = theCondition.awaitUntil(deadline);
* else
* return false;
* }
- * // ...
+ * // ...
* }
* </pre>
*
* <p><b>Implementation Considerations</b>
+ *
* <p>The current thread is assumed to hold the lock associated with this
- * <tt>Condition</tt> when this method is called.
+ * {@code Condition} when this method is called.
* It is up to the implementation to determine if this is
- * the case and if not, how to respond. Typically, an exception will be
+ * the case and if not, how to respond. Typically, an exception will be
* thrown (such as {@link IllegalMonitorStateException}) and the
* implementation must document that fact.
*
@@ -401,13 +407,11 @@ public interface Condition {
* must ensure that the signal is redirected to another waiting thread, if
* there is one.
*
- *
* @param deadline the absolute time to wait until
- * @return <tt>false</tt> if the deadline has
- * elapsed upon return, else <tt>true</tt>.
- *
- * @throws InterruptedException if the current thread is interrupted (and
- * interruption of thread suspension is supported).
+ * @return {@code false} if the deadline has elapsed upon return, else
+ * {@code true}
+ * @throws InterruptedException if the current thread is interrupted
+ * (and interruption of thread suspension is supported)
*/
boolean awaitUntil(Date deadline) throws InterruptedException;
@@ -416,8 +420,8 @@ public interface Condition {
*
* <p>If any threads are waiting on this condition then one
* is selected for waking up. That thread must then re-acquire the
- * lock before returning from <tt>await</tt>.
- **/
+ * lock before returning from {@code await}.
+ */
void signal();
/**
@@ -425,15 +429,7 @@ public interface Condition {
*
* <p>If any threads are waiting on this condition then they are
* all woken up. Each thread must re-acquire the lock before it can
- * return from <tt>await</tt>.
- **/
+ * return from {@code await}.
+ */
void signalAll();
-
}
-
-
-
-
-
-
-
diff --git a/concurrent/src/main/java/java/util/concurrent/locks/Lock.java b/concurrent/src/main/java/java/util/concurrent/locks/Lock.java
index f07b72e..4b9abd6 100644
--- a/concurrent/src/main/java/java/util/concurrent/locks/Lock.java
+++ b/concurrent/src/main/java/java/util/concurrent/locks/Lock.java
@@ -8,8 +8,8 @@ package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
/**
- * <tt>Lock</tt> implementations provide more extensive locking
- * operations than can be obtained using <tt>synchronized</tt> methods
+ * {@code Lock} implementations provide more extensive locking
+ * operations than can be obtained using {@code synchronized} methods
* and statements. They allow more flexible structuring, may have
* quite different properties, and may support multiple associated
* {@link Condition} objects.
@@ -19,17 +19,16 @@ import java.util.concurrent.TimeUnit;
* shared resource: only one thread at a time can acquire the lock and
* all access to the shared resource requires that the lock be
* acquired first. However, some locks may allow concurrent access to
- * a shared resource, such as the read lock of a {@link
- * ReadWriteLock}.
+ * a shared resource, such as the read lock of a {@link ReadWriteLock}.
*
- * <p>The use of <tt>synchronized</tt> methods or statements provides
+ * <p>The use of {@code synchronized} methods or statements provides
* access to the implicit monitor lock associated with every object, but
* forces all lock acquisition and release to occur in a block-structured way:
* when multiple locks are acquired they must be released in the opposite
* order, and all locks must be released in the same lexical scope in which
* they were acquired.
*
- * <p>While the scoping mechanism for <tt>synchronized</tt> methods
+ * <p>While the scoping mechanism for {@code synchronized} methods
* and statements makes it much easier to program with monitor locks,
* and helps avoid many common programming errors involving locks,
* there are occasions where you need to work with locks in a more
@@ -38,18 +37,18 @@ import java.util.concurrent.TimeUnit;
* &quot;hand-over-hand&quot; or &quot;chain locking&quot;: you
* acquire the lock of node A, then node B, then release A and acquire
* C, then release B and acquire D and so on. Implementations of the
- * <tt>Lock</tt> interface enable the use of such techniques by
+ * {@code Lock} interface enable the use of such techniques by
* allowing a lock to be acquired and released in different scopes,
* and allowing multiple locks to be acquired and released in any
* order.
*
* <p>With this increased flexibility comes additional
* responsibility. The absence of block-structured locking removes the
- * automatic release of locks that occurs with <tt>synchronized</tt>
+ * automatic release of locks that occurs with {@code synchronized}
* methods and statements. In most cases, the following idiom
* should be used:
*
- * <pre><tt> Lock l = ...;
+ * <pre><tt> Lock l = ...;
* l.lock();
* try {
* // access the resource protected by this lock
@@ -63,39 +62,42 @@ import java.util.concurrent.TimeUnit;
* held is protected by try-finally or try-catch to ensure that the
* lock is released when necessary.
*
- * <p><tt>Lock</tt> implementations provide additional functionality
- * over the use of <tt>synchronized</tt> methods and statements by
+ * <p>{@code Lock} implementations provide additional functionality
+ * over the use of {@code synchronized} methods and statements by
* providing a non-blocking attempt to acquire a lock ({@link
* #tryLock()}), an attempt to acquire the lock that can be
* interrupted ({@link #lockInterruptibly}, and an attempt to acquire
* the lock that can timeout ({@link #tryLock(long, TimeUnit)}).
*
- * <p>A <tt>Lock</tt> class can also provide behavior and semantics
+ * <p>A {@code Lock} class can also provide behavior and semantics
* that is quite different from that of the implicit monitor lock,
* such as guaranteed ordering, non-reentrant usage, or deadlock
* detection. If an implementation provides such specialized semantics
* then the implementation must document those semantics.
*
- * <p>Note that <tt>Lock</tt> instances are just normal objects and can
- * themselves be used as the target in a <tt>synchronized</tt> statement.
+ * <p>Note that {@code Lock} instances are just normal objects and can
+ * themselves be used as the target in a {@code synchronized} statement.
* Acquiring the
- * monitor lock of a <tt>Lock</tt> instance has no specified relationship
- * with invoking any of the {@link #lock} methods of that instance.
- * It is recommended that to avoid confusion you never use <tt>Lock</tt>
+ * monitor lock of a {@code Lock} instance has no specified relationship
+ * with invoking any of the {@link #lock} methods of that instance.
+ * It is recommended that to avoid confusion you never use {@code Lock}
* instances in this way, except within their own implementation.
*
- * <p>Except where noted, passing a <tt>null</tt> value for any
+ * <p>Except where noted, passing a {@code null} value for any
* parameter will result in a {@link NullPointerException} being
* thrown.
*
* <h3>Memory Synchronization</h3>
- * <p>All <tt>Lock</tt> implementations <em>must</em> enforce the same
- * memory synchronization semantics as provided by the built-in monitor lock:
+ *
+ * <p>All {@code Lock} implementations <em>must</em> enforce the same
+ * memory synchronization semantics as provided by the built-in monitor
+ * lock, as described in <a href="http://java.sun.com/docs/books/jls/">
+ * The Java Language Specification, Third Edition (17.4 Memory Model)</a>:
* <ul>
- * <li>A successful lock operation acts like a successful
- * <tt>monitorEnter</tt> action
- * <li>A successful <tt>unlock</tt> operation acts like a successful
- * <tt>monitorExit</tt> action
+ * <li>A successful {@code lock} operation has the same memory
+ * synchronization effects as a successful <em>Lock</em> action.
+ * <li>A successful {@code unlock} operation has the same
+ * memory synchronization effects as a successful <em>Unlock</em> action.
* </ul>
*
* Unsuccessful locking and unlocking operations, and reentrant
@@ -108,7 +110,7 @@ import java.util.concurrent.TimeUnit;
* non-interruptible, and timed) may differ in their performance
* characteristics, ordering guarantees, or other implementation
* qualities. Further, the ability to interrupt the <em>ongoing</em>
- * acquisition of a lock may not be available in a given <tt>Lock</tt>
+ * acquisition of a lock may not be available in a given {@code Lock}
* class. Consequently, an implementation is not required to define
* exactly the same guarantees or semantics for all three forms of
* lock acquisition, nor is it required to support interruption of an
@@ -119,12 +121,11 @@ import java.util.concurrent.TimeUnit;
* acquisition is supported: which is either totally, or only on
* method entry.
*
- * <p>As interruption generally implies cancellation, and checks for
+ * <p>As interruption generally implies cancellation, and checks for
* interruption are often infrequent, an implementation can favor responding
* to an interrupt over normal method return. This is true even if it can be
* shown that the interrupt occurred after another action may have unblocked
- * the thread. An implementation should document this behavior.
- *
+ * the thread. An implementation should document this behavior.
*
* @see ReentrantLock
* @see Condition
@@ -132,45 +133,50 @@ import java.util.concurrent.TimeUnit;
*
* @since 1.5
* @author Doug Lea
- *
- **/
+ */
public interface Lock {
/**
* Acquires the lock.
- * <p>If the lock is not available then
- * the current thread becomes disabled for thread scheduling
- * purposes and lies dormant until the lock has been acquired.
+ *
+ * <p>If the lock is not available then the current thread becomes
+ * disabled for thread scheduling purposes and lies dormant until the
+ * lock has been acquired.
+ *
* <p><b>Implementation Considerations</b>
- * <p>A <tt>Lock</tt> implementation may be able to detect
- * erroneous use of the lock, such as an invocation that would cause
- * deadlock, and may throw an (unchecked) exception in such circumstances.
- * The circumstances and the exception type must be documented by that
- * <tt>Lock</tt> implementation.
*
- **/
+ * <p>A {@code Lock} implementation may be able to detect erroneous use
+ * of the lock, such as an invocation that would cause deadlock, and
+ * may throw an (unchecked) exception in such circumstances. The
+ * circumstances and the exception type must be documented by that
+ * {@code Lock} implementation.
+ */
void lock();
/**
- * Acquires the lock unless the current thread is
- * {@link Thread#interrupt interrupted}.
+ * Acquires the lock unless the current thread is
+ * {@linkplain Thread#interrupt interrupted}.
+ *
* <p>Acquires the lock if it is available and returns immediately.
- * <p>If the lock is not available then
- * the current thread becomes disabled for thread scheduling
- * purposes and lies dormant until one of two things happens:
+ *
+ * <p>If the lock is not available then the current thread becomes
+ * disabled for thread scheduling purposes and lies dormant until
+ * one of two things happens:
+ *
* <ul>
* <li>The lock is acquired by the current thread; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread, and interruption of lock acquisition is supported.
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
+ * current thread, and interruption of lock acquisition is supported.
* </ul>
+ *
* <p>If the current thread:
* <ul>
- * <li>has its interrupted status set on entry to this method; or
- * <li>is {@link Thread#interrupt interrupted} while acquiring
- * the lock, and interruption of lock acquisition is supported,
+ * <li>has its interrupted status set on entry to this method; or
+ * <li>is {@linkplain Thread#interrupt interrupted} while acquiring the
+ * lock, and interruption of lock acquisition is supported,
* </ul>
- * then {@link InterruptedException} is thrown and the current thread's
- * interrupted status is cleared.
+ * then {@link InterruptedException} is thrown and the current thread's
+ * interrupted status is cleared.
*
* <p><b>Implementation Considerations</b>
*
@@ -183,28 +189,26 @@ public interface Lock {
* <p>An implementation can favor responding to an interrupt over
* normal method return.
*
- * <p>A <tt>Lock</tt> implementation may be able to detect
+ * <p>A {@code Lock} implementation may be able to detect
* erroneous use of the lock, such as an invocation that would
* cause deadlock, and may throw an (unchecked) exception in such
* circumstances. The circumstances and the exception type must
- * be documented by that <tt>Lock</tt> implementation.
- *
- * @throws InterruptedException if the current thread is interrupted
- * while acquiring the lock (and interruption of lock acquisition is
- * supported).
- *
- * @see Thread#interrupt
+ * be documented by that {@code Lock} implementation.
*
- **/
+ * @throws InterruptedException if the current thread is
+ * interrupted while acquiring the lock (and interruption
+ * of lock acquisition is supported).
+ */
void lockInterruptibly() throws InterruptedException;
-
/**
* Acquires the lock only if it is free at the time of invocation.
+ *
* <p>Acquires the lock if it is available and returns immediately
- * with the value <tt>true</tt>.
- * If the lock is not available then this method will return
- * immediately with the value <tt>false</tt>.
+ * with the value {@code true}.
+ * If the lock is not available then this method will return
+ * immediately with the value {@code false}.
+ *
* <p>A typical usage idiom for this method would be:
* <pre>
* Lock lock = ...;
@@ -221,109 +225,103 @@ public interface Lock {
* This usage ensures that the lock is unlocked if it was acquired, and
* doesn't try to unlock if the lock was not acquired.
*
- * @return <tt>true</tt> if the lock was acquired and <tt>false</tt>
- * otherwise.
- **/
+ * @return {@code true} if the lock was acquired and
+ * {@code false} otherwise
+ */
boolean tryLock();
/**
* Acquires the lock if it is free within the given waiting time and the
- * current thread has not been {@link Thread#interrupt interrupted}.
+ * current thread has not been {@linkplain Thread#interrupt interrupted}.
*
* <p>If the lock is available this method returns immediately
- * with the value <tt>true</tt>.
+ * with the value {@code true}.
* If the lock is not available then
- * the current thread becomes disabled for thread scheduling
+ * the current thread becomes disabled for thread scheduling
* purposes and lies dormant until one of three things happens:
* <ul>
* <li>The lock is acquired by the current thread; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread, and interruption of lock acquisition is supported; or
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
+ * current thread, and interruption of lock acquisition is supported; or
* <li>The specified waiting time elapses
* </ul>
- * <p>If the lock is acquired then the value <tt>true</tt> is returned.
+ *
+ * <p>If the lock is acquired then the value {@code true} is returned.
+ *
* <p>If the current thread:
* <ul>
- * <li>has its interrupted status set on entry to this method; or
- * <li>is {@link Thread#interrupt interrupted} while acquiring
- * the lock, and interruption of lock acquisition is supported,
+ * <li>has its interrupted status set on entry to this method; or
+ * <li>is {@linkplain Thread#interrupt interrupted} while acquiring
+ * the lock, and interruption of lock acquisition is supported,
* </ul>
- * then {@link InterruptedException} is thrown and the current thread's
- * interrupted status is cleared.
- * <p>If the specified waiting time elapses then the value <tt>false</tt>
+ * then {@link InterruptedException} is thrown and the current thread's
+ * interrupted status is cleared.
+ *
+ * <p>If the specified waiting time elapses then the value {@code false}
* is returned.
- * If the time is
+ * If the time is
* less than or equal to zero, the method will not wait at all.
*
* <p><b>Implementation Considerations</b>
+ *
* <p>The ability to interrupt a lock acquisition in some implementations
- * may not be possible, and if possible may
- * be an expensive operation.
+ * may not be possible, and if possible may
+ * be an expensive operation.
* The programmer should be aware that this may be the case. An
* implementation should document when this is the case.
- * <p>An implementation can favor responding to an interrupt over normal
+ *
+ * <p>An implementation can favor responding to an interrupt over normal
* method return, or reporting a timeout.
- * <p>A <tt>Lock</tt> implementation may be able to detect
- * erroneous use of the lock, such as an invocation that would cause
- * deadlock, and may throw an (unchecked) exception in such circumstances.
- * The circumstances and the exception type must be documented by that
- * <tt>Lock</tt> implementation.
+ *
+ * <p>A {@code Lock} implementation may be able to detect
+ * erroneous use of the lock, such as an invocation that would cause
+ * deadlock, and may throw an (unchecked) exception in such circumstances.
+ * The circumstances and the exception type must be documented by that
+ * {@code Lock} implementation.
*
* @param time the maximum time to wait for the lock
- * @param unit the time unit of the <tt>time</tt> argument.
- * @return <tt>true</tt> if the lock was acquired and <tt>false</tt>
- * if the waiting time elapsed before the lock was acquired.
+ * @param unit the time unit of the {@code time} argument
+ * @return {@code true} if the lock was acquired and {@code false}
+ * if the waiting time elapsed before the lock was acquired
*
* @throws InterruptedException if the current thread is interrupted
- * while acquiring the lock (and interruption of lock acquisition is
- * supported).
- *
- * @see Thread#interrupt
- *
- **/
+ * while acquiring the lock (and interruption of lock
+ * acquisition is supported)
+ */
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
/**
* Releases the lock.
+ *
* <p><b>Implementation Considerations</b>
- * <p>A <tt>Lock</tt> implementation will usually impose
+ *
+ * <p>A {@code Lock} implementation will usually impose
* restrictions on which thread can release a lock (typically only the
* holder of the lock can release it) and may throw
* an (unchecked) exception if the restriction is violated.
* Any restrictions and the exception
- * type must be documented by that <tt>Lock</tt> implementation.
- **/
+ * type must be documented by that {@code Lock} implementation.
+ */
void unlock();
/**
- * Returns a new {@link Condition} instance that is bound to this
- * <tt>Lock</tt> instance.
- * <p>Before waiting on the condition the lock must be held by the
- * current thread.
- * A call to {@link Condition#await()} will atomically release the lock
+ * Returns a new {@link Condition} instance that is bound to this
+ * {@code Lock} instance.
+ *
+ * <p>Before waiting on the condition the lock must be held by the
+ * current thread.
+ * A call to {@link Condition#await()} will atomically release the lock
* before waiting and re-acquire the lock before the wait returns.
+ *
* <p><b>Implementation Considerations</b>
- * <p>The exact operation of the {@link Condition} instance depends on the
- * <tt>Lock</tt> implementation and must be documented by that
+ *
+ * <p>The exact operation of the {@link Condition} instance depends on
+ * the {@code Lock} implementation and must be documented by that
* implementation.
- *
- * @return A new {@link Condition} instance for this <tt>Lock</tt>
- * instance.
- * @throws UnsupportedOperationException if this <tt>Lock</tt>
- * implementation does not support conditions.
- **/
+ *
+ * @return A new {@link Condition} instance for this {@code Lock} instance
+ * @throws UnsupportedOperationException if this {@code Lock}
+ * implementation does not support conditions
+ */
Condition newCondition();
-
}
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java b/concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java
index 8603785..b55b874 100644
--- a/concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java
+++ b/concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java
@@ -13,48 +13,56 @@ import sun.misc.Unsafe;
* Basic thread blocking primitives for creating locks and other
* synchronization classes.
*
- * <p>This class associates with each thread that uses it, a permit
+ * <p>This class associates, with each thread that uses it, a permit
* (in the sense of the {@link java.util.concurrent.Semaphore
- * Semaphore} class). A call to <tt>park</tt> will return immediately
+ * Semaphore} class). A call to {@code park} will return immediately
* if the permit is available, consuming it in the process; otherwise
- * it <em>may</em> block. A call to <tt>unpark</tt> makes the permit
+ * it <em>may</em> block. A call to {@code unpark} makes the permit
* available, if it was not already available. (Unlike with Semaphores
* though, permits do not accumulate. There is at most one.)
*
- * <p>Methods <tt>park</tt> and <tt>unpark</tt> provide efficient
+ * <p>Methods {@code park} and {@code unpark} provide efficient
* means of blocking and unblocking threads that do not encounter the
- * problems that cause the deprecated methods <tt>Thread.suspend</tt>
- * and <tt>Thread.resume</tt> to be unusable for such purposes: Races
- * between one thread invoking <tt>park</tt> and another thread trying
- * to <tt>unpark</tt> it will preserve liveness, due to the
- * permit. Additionally, <tt>park</tt> will return if the caller's
+ * problems that cause the deprecated methods {@code Thread.suspend}
+ * and {@code Thread.resume} to be unusable for such purposes: Races
+ * between one thread invoking {@code park} and another thread trying
+ * to {@code unpark} it will preserve liveness, due to the
+ * permit. Additionally, {@code park} will return if the caller's
* thread was interrupted, and timeout versions are supported. The
- * <tt>park</tt> method may also return at any other time, for "no
+ * {@code park} method may also return at any other time, for "no
* reason", so in general must be invoked within a loop that rechecks
- * conditions upon return. In this sense <tt>park</tt> serves as an
+ * conditions upon return. In this sense {@code park} serves as an
* optimization of a "busy wait" that does not waste as much time
- * spinning, but must be paired with an <tt>unpark</tt> to be
+ * spinning, but must be paired with an {@code unpark} to be
* effective.
*
* <p>These methods are designed to be used as tools for creating
* higher-level synchronization utilities, and are not in themselves
- * useful for most concurrency control applications.
+ * useful for most concurrency control applications. The {@code park}
+ * method is designed for use only in constructions of the form:
+ * <pre>while (!canProceed()) { ... LockSupport.park(this); }</pre>
+ * where neither {@code canProceed} nor any other actions prior to the
+ * call to {@code park} entail locking or blocking. Because only one
+ * permit is associated with each thread, any intermediary uses of
+ * {@code park} could interfere with its intended effects.
*
- * <p><b>Sample Usage.</b> Here is a sketch of a First-in-first-out
- * non-reentrant lock class.
- * <pre>
- * private AtomicBoolean locked = new AtomicBoolean(false);
- * private Queue&lt;Thread&gt; waiters = new ConcurrentLinkedQueue&lt;Thread&gt;();
+ * <p><b>Sample Usage.</b> Here is a sketch of a first-in-first-out
+ * non-reentrant lock class:
+ * <pre>{@code
+ * class FIFOMutex {
+ * private final AtomicBoolean locked = new AtomicBoolean(false);
+ * private final Queue<Thread> waiters
+ * = new ConcurrentLinkedQueue<Thread>();
*
- * public void lock() {
+ * public void lock() {
* boolean wasInterrupted = false;
* Thread current = Thread.currentThread();
* waiters.add(current);
*
* // Block while not first in queue or cannot acquire lock
- * while (waiters.peek() != current ||
- * !locked.compareAndSet(false, true)) {
- * LockSupport.park();
+ * while (waiters.peek() != current ||
+ * !locked.compareAndSet(false, true)) {
+ * LockSupport.park(this);
* if (Thread.interrupted()) // ignore interrupts while waiting
* wasInterrupted = true;
* }
@@ -67,9 +75,8 @@ import sun.misc.Unsafe;
* public void unlock() {
* locked.set(false);
* LockSupport.unpark(waiters.peek());
- * }
- * }
- * </pre>
+ * }
+ * }}</pre>
*/
@SuppressWarnings("all")
public class LockSupport {
@@ -80,14 +87,15 @@ public class LockSupport {
// END android-changed
/**
- * Make available the permit for the given thread, if it
+ * Makes available the permit for the given thread, if it
* was not already available. If the thread was blocked on
- * <tt>park</tt> then it will unblock. Otherwise, its next call
- * to <tt>park</tt> is guaranteed not to block. This operation
+ * {@code park} then it will unblock. Otherwise, its next call
+ * to {@code park} is guaranteed not to block. This operation
* is not guaranteed to have any effect at all if the given
* thread has not been started.
- * @param thread the thread to unpark, or <tt>null</tt>, in which case
- * this operation has no effect.
+ *
+ * @param thread the thread to unpark, or {@code null}, in which case
+ * this operation has no effect
*/
public static void unpark(Thread thread) {
if (thread != null)
@@ -97,20 +105,26 @@ public class LockSupport {
/**
* Disables the current thread for thread scheduling purposes unless the
* permit is available.
- * <p>If the permit is available then it is consumed and the call returns
- * immediately; otherwise
- * the current thread becomes disabled for thread scheduling
- * purposes and lies dormant until one of three things happens:
+ *
+ * <p>If the permit is available then it is consumed and the call
+ * returns immediately; otherwise the current thread becomes disabled
+ * for thread scheduling purposes and lies dormant until one of three
+ * things happens:
+ *
* <ul>
- * <li>Some other thread invokes <tt>unpark</tt> with the current thread
- * as the target; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread; or
+ *
+ * <li>Some other thread invokes {@link #unpark unpark} with the
+ * current thread as the target; or
+ *
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
+ * the current thread; or
+ *
* <li>The call spuriously (that is, for no reason) returns.
* </ul>
- * <p>This method does <em>not</em> report which of these caused the
- * method to return. Callers should re-check the conditions which caused
- * the thread to park in the first place. Callers may also determine,
+ *
+ * <p>This method does <em>not</em> report which of these caused the
+ * method to return. Callers should re-check the conditions which caused
+ * the thread to park in the first place. Callers may also determine,
* for example, the interrupt status of the thread upon return.
*/
public static void park() {
@@ -120,21 +134,27 @@ public class LockSupport {
/**
* Disables the current thread for thread scheduling purposes, for up to
* the specified waiting time, unless the permit is available.
- * <p>If the permit is available then it is consumed and the call returns
- * immediately; otherwise
- * the current thread becomes disabled for thread scheduling
- * purposes and lies dormant until one of four things happens:
+ *
+ * <p>If the permit is available then it is consumed and the call
+ * returns immediately; otherwise the current thread becomes disabled
+ * for thread scheduling purposes and lies dormant until one of four
+ * things happens:
+ *
* <ul>
- * <li>Some other thread invokes <tt>unpark</tt> with the current thread
- * as the target; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread; or
+ * <li>Some other thread invokes {@link #unpark unpark} with the
+ * current thread as the target; or
+ *
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
+ * the current thread; or
+ *
* <li>The specified waiting time elapses; or
+ *
* <li>The call spuriously (that is, for no reason) returns.
* </ul>
- * <p>This method does <em>not</em> report which of these caused the
- * method to return. Callers should re-check the conditions which caused
- * the thread to park in the first place. Callers may also determine,
+ *
+ * <p>This method does <em>not</em> report which of these caused the
+ * method to return. Callers should re-check the conditions which caused
+ * the thread to park in the first place. Callers may also determine,
* for example, the interrupt status of the thread, or the elapsed time
* upon return.
*
@@ -142,37 +162,40 @@ public class LockSupport {
*/
public static void parkNanos(long nanos) {
if (nanos > 0)
- unsafe.park(false, nanos);
+ unsafe.park(false, nanos);
}
/**
* Disables the current thread for thread scheduling purposes, until
* the specified deadline, unless the permit is available.
- * <p>If the permit is available then it is consumed and the call returns
- * immediately; otherwise
- * the current thread becomes disabled for thread scheduling
- * purposes and lies dormant until one of four things happens:
+ *
+ * <p>If the permit is available then it is consumed and the call
+ * returns immediately; otherwise the current thread becomes disabled
+ * for thread scheduling purposes and lies dormant until one of four
+ * things happens:
+ *
* <ul>
- * <li>Some other thread invokes <tt>unpark</tt> with the current thread
- * as the target; or
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread; or
+ * <li>Some other thread invokes {@link #unpark unpark} with the
+ * current thread as the target; or
+ *
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
+ * the current thread; or
+ *
* <li>The specified deadline passes; or
+ *
* <li>The call spuriously (that is, for no reason) returns.
* </ul>
- * <p>This method does <em>not</em> report which of these caused the
- * method to return. Callers should re-check the conditions which caused
- * the thread to park in the first place. Callers may also determine,
+ *
+ * <p>This method does <em>not</em> report which of these caused the
+ * method to return. Callers should re-check the conditions which caused
+ * the thread to park in the first place. Callers may also determine,
* for example, the interrupt status of the thread, or the current time
* upon return.
*
- * @param deadline the absolute time, in milliseconds from the Epoch, to
- * wait until
+ * @param deadline the absolute time, in milliseconds from the Epoch,
+ * to wait until
*/
public static void parkUntil(long deadline) {
- unsafe.park(true, deadline);
+ unsafe.park(true, deadline);
}
-
}
-
-
diff --git a/concurrent/src/main/java/java/util/concurrent/locks/ReadWriteLock.java b/concurrent/src/main/java/java/util/concurrent/locks/ReadWriteLock.java
index c7116d5..484f68d 100644
--- a/concurrent/src/main/java/java/util/concurrent/locks/ReadWriteLock.java
+++ b/concurrent/src/main/java/java/util/concurrent/locks/ReadWriteLock.java
@@ -12,11 +12,18 @@ package java.util.concurrent.locks;
* The {@link #readLock read lock} may be held simultaneously by
* multiple reader threads, so long as there are no writers. The
* {@link #writeLock write lock} is exclusive.
- *
+ *
+ * <p>All <tt>ReadWriteLock</tt> implementations must guarantee that
+ * the memory synchronization effects of <tt>writeLock</tt> operations
+ * (as specified in the {@link Lock} interface) also hold with respect
+ * to the associated <tt>readLock</tt>. That is, a thread successfully
+ * acquiring the read lock will see all updates made upon previous
+ * release of the write lock.
+ *
* <p>A read-write lock allows for a greater level of concurrency in
- * accessing shared data, than that permitted by a mutual exclusion lock.
+ * accessing shared data than that permitted by a mutual exclusion lock.
* It exploits the fact that while only a single thread at a time (a
- * <em>writer</em> thread) can modify the shared data, in many cases any
+ * <em>writer</em> thread) can modify the shared data, in many cases any
* number of threads can concurrently read the data (hence <em>reader</em>
* threads).
* In theory, the increase in concurrency permitted by the use of a read-write
@@ -27,7 +34,7 @@ package java.util.concurrent.locks;
*
* <p>Whether or not a read-write lock will improve performance over the use
* of a mutual exclusion lock depends on the frequency that the data is
- * read compared to being modified, the duration of the read and write
+ * read compared to being modified, the duration of the read and write
* operations, and the contention for the data - that is, the number of
* threads that will try to read or write the data at the same time.
* For example, a collection that is initially populated with data and
@@ -56,14 +63,14 @@ package java.util.concurrent.locks;
* lengthy delays for a write if the readers are frequent and long-lived as
* expected. Fair, or &quot;in-order&quot; implementations are also possible.
*
- * <li>Determining whether readers that request the read lock while a
+ * <li>Determining whether readers that request the read lock while a
* reader is active and a writer is waiting, are granted the read lock.
* Preference to the reader can delay the writer indefinitely, while
- * preference to the write can reduce the potential for concurrency.
+ * preference to the writer can reduce the potential for concurrency.
*
* <li>Determining whether the locks are reentrant: can a thread with the
- * write lock reacquire it? can it acquire a read lock while holding the
- * write lock? is the read lock itself reentrant?
+ * write lock reacquire it? Can it acquire a read lock while holding the
+ * write lock? Is the read lock itself reentrant?
*
* <li>Can the write lock be downgraded to a read lock without allowing
* an intervening writer? Can a read lock be upgraded to a write lock,
diff --git a/concurrent/src/main/java/java/util/concurrent/locks/ReentrantLock.java b/concurrent/src/main/java/java/util/concurrent/locks/ReentrantLock.java
index bf3d653..bf2ac38 100644
--- a/concurrent/src/main/java/java/util/concurrent/locks/ReentrantLock.java
+++ b/concurrent/src/main/java/java/util/concurrent/locks/ReentrantLock.java
@@ -12,19 +12,19 @@ import java.util.concurrent.atomic.*;
/**
* A reentrant mutual exclusion {@link Lock} with the same basic
* behavior and semantics as the implicit monitor lock accessed using
- * <tt>synchronized</tt> methods and statements, but with extended
+ * {@code synchronized} methods and statements, but with extended
* capabilities.
*
- * <p> A <tt>ReentrantLock</tt> is <em>owned</em> by the thread last
+ * <p>A {@code ReentrantLock} is <em>owned</em> by the thread last
* successfully locking, but not yet unlocking it. A thread invoking
- * <tt>lock</tt> will return, successfully acquiring the lock, when
+ * {@code lock} will return, successfully acquiring the lock, when
* the lock is not owned by another thread. The method will return
* immediately if the current thread already owns the lock. This can
* be checked using methods {@link #isHeldByCurrentThread}, and {@link
- * #getHoldCount}.
+ * #getHoldCount}.
*
- * <p> The constructor for this class accepts an optional
- * <em>fairness</em> parameter. When set <tt>true</tt>, under
+ * <p>The constructor for this class accepts an optional
+ * <em>fairness</em> parameter. When set {@code true}, under
* contention, locks favor granting access to the longest-waiting
* thread. Otherwise this lock does not guarantee any particular
* access order. Programs using fair locks accessed by many threads
@@ -36,9 +36,12 @@ import java.util.concurrent.atomic.*;
* fair lock may obtain it multiple times in succession while other
* active threads are not progressing and not currently holding the
* lock.
+ * Also note that the untimed {@link #tryLock() tryLock} method does not
+ * honor the fairness setting. It will succeed if the lock
+ * is available even if other threads are waiting.
*
- * <p> It is recommended practice to <em>always</em> immediately
- * follow a call to <tt>lock</tt> with a <tt>try</tt> block, most
+ * <p>It is recommended practice to <em>always</em> immediately
+ * follow a call to {@code lock} with a {@code try} block, most
* typically in a before/after construction such as:
*
* <pre>
@@ -46,7 +49,7 @@ import java.util.concurrent.atomic.*;
* private final ReentrantLock lock = new ReentrantLock();
* // ...
*
- * public void m() {
+ * public void m() {
* lock.lock(); // block until condition holds
* try {
* // ... method body
@@ -58,21 +61,21 @@ import java.util.concurrent.atomic.*;
* </pre>
*
* <p>In addition to implementing the {@link Lock} interface, this
- * class defines methods <tt>isLocked</tt> and
- * <tt>getLockQueueLength</tt>, as well as some associated
- * <tt>protected</tt> access methods that may be useful for
+ * class defines methods {@code isLocked} and
+ * {@code getLockQueueLength}, as well as some associated
+ * {@code protected} access methods that may be useful for
* instrumentation and monitoring.
*
- * <p> Serialization of this class behaves in the same way as built-in
+ * <p>Serialization of this class behaves in the same way as built-in
* locks: a deserialized lock is in the unlocked state, regardless of
* its state when serialized.
*
- * <p> This lock supports a maximum of 2147483648 recursive locks by
- * the same thread.
+ * <p>This lock supports a maximum of 2147483647 recursive locks by
+ * the same thread. Attempts to exceed this limit result in
+ * {@link Error} throws from locking methods.
*
* @since 1.5
* @author Doug Lea
- *
*/
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
@@ -84,32 +87,34 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* into fair and nonfair versions below. Uses AQS state to
* represent the number of holds on the lock.
*/
- static abstract class Sync extends AbstractQueuedSynchronizer {
- /** Current owner thread */
- transient Thread owner;
+ static abstract class Sync extends AbstractQueuedSynchronizer {
+ private static final long serialVersionUID = -5179523762034025860L;
/**
- * Perform {@link Lock#lock}. The main reason for subclassing
+ * Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
- /**
- * Perform non-fair tryLock. tryAcquire is
+ /**
+ * Performs non-fair tryLock. tryAcquire is
* implemented in subclasses, but both need nonfair
- * try for trylock method
+ * try for trylock method.
*/
- final boolean nonfairTryAcquire(int acquires) {
+ final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
- owner = current;
+ setExclusiveOwnerThread(current);
return true;
}
}
- else if (current == owner) {
- setState(c+acquires);
+ else if (current == getExclusiveOwnerThread()) {
+ int nextc = c + acquires;
+ if (nextc < 0) // overflow
+ throw new Error("Maximum lock count exceeded");
+ setState(nextc);
return true;
}
return false;
@@ -117,19 +122,21 @@ public class ReentrantLock implements Lock, java.io.Serializable {
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
- if (Thread.currentThread() != owner)
+ if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
- owner = null;
+ setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
protected final boolean isHeldExclusively() {
- return getState() != 0 && owner == Thread.currentThread();
+ // While we must in general read state before owner,
+ // we don't need to do so to check if current thread is owner
+ return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
@@ -139,23 +146,19 @@ public class ReentrantLock implements Lock, java.io.Serializable {
// Methods relayed from outer class
final Thread getOwner() {
- int c = getState();
- Thread o = owner;
- return (c == 0)? null : o;
+ return getState() == 0 ? null : getExclusiveOwnerThread();
}
-
+
final int getHoldCount() {
- int c = getState();
- Thread o = owner;
- return (o == Thread.currentThread())? c : 0;
+ return isHeldExclusively() ? getState() : 0;
}
-
+
final boolean isLocked() {
return getState() != 0;
}
/**
- * Reconstitute this lock instance from a stream
+ * Reconstitutes this lock instance from a stream.
* @param s the stream
*/
private void readObject(java.io.ObjectInputStream s)
@@ -169,18 +172,20 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* Sync object for non-fair locks
*/
final static class NonfairSync extends Sync {
+ private static final long serialVersionUID = 7316153563782823691L;
+
/**
- * Perform lock. Try immediate barge, backing up to normal
+ * Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
- owner = Thread.currentThread();
+ setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
- protected final boolean tryAcquire(int acquires) {
+ protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
@@ -188,28 +193,32 @@ public class ReentrantLock implements Lock, java.io.Serializable {
/**
* Sync object for fair locks
*/
- final static class FairSync extends Sync {
- final void lock() {
- acquire(1);
+ final static class FairSync extends Sync {
+ private static final long serialVersionUID = -3000897897090466540L;
+
+ final void lock() {
+ acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
- protected final boolean tryAcquire(int acquires) {
+ protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
- Thread first = getFirstQueuedThread();
- if ((first == null || first == current) &&
+ if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
- owner = current;
+ setExclusiveOwnerThread(current);
return true;
}
}
- else if (current == owner) {
- setState(c+acquires);
+ else if (current == getExclusiveOwnerThread()) {
+ int nextc = c + acquires;
+ if (nextc < 0)
+ throw new Error("Maximum lock count exceeded");
+ setState(nextc);
return true;
}
return false;
@@ -217,89 +226,88 @@ public class ReentrantLock implements Lock, java.io.Serializable {
}
/**
- * Creates an instance of <tt>ReentrantLock</tt>.
- * This is equivalent to using <tt>ReentrantLock(false)</tt>.
+ * Creates an instance of {@code ReentrantLock}.
+ * This is equivalent to using {@code ReentrantLock(false)}.
*/
- public ReentrantLock() {
+ public ReentrantLock() {
sync = new NonfairSync();
}
/**
- * Creates an instance of <tt>ReentrantLock</tt> with the
+ * Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
- * @param fair true if this lock will be fair; else false
+ *
+ * @param fair {@code true} if this lock should use a fair ordering policy
*/
- public ReentrantLock(boolean fair) {
+ public ReentrantLock(boolean fair) {
sync = (fair)? new FairSync() : new NonfairSync();
}
/**
- * Acquires the lock.
+ * Acquires the lock.
*
- * <p>Acquires the lock if it is not held by another thread and returns
+ * <p>Acquires the lock if it is not held by another thread and returns
* immediately, setting the lock hold count to one.
*
- * <p>If the current thread
- * already holds the lock then the hold count is incremented by one and
- * the method returns immediately.
+ * <p>If the current thread already holds the lock then the hold
+ * count is incremented by one and the method returns immediately.
*
* <p>If the lock is held by another thread then the
- * current thread becomes disabled for thread scheduling
+ * current thread becomes disabled for thread scheduling
* purposes and lies dormant until the lock has been acquired,
- * at which time the lock hold count is set to one.
+ * at which time the lock hold count is set to one.
*/
public void lock() {
sync.lock();
}
/**
- * Acquires the lock unless the current thread is
- * {@link Thread#interrupt interrupted}.
+ * Acquires the lock unless the current thread is
+ * {@linkplain Thread#interrupt interrupted}.
*
- * <p>Acquires the lock if it is not held by another thread and returns
+ * <p>Acquires the lock if it is not held by another thread and returns
* immediately, setting the lock hold count to one.
*
- * <p>If the current thread already holds this lock then the hold count
+ * <p>If the current thread already holds this lock then the hold count
* is incremented by one and the method returns immediately.
*
* <p>If the lock is held by another thread then the
- * current thread becomes disabled for thread scheduling
+ * current thread becomes disabled for thread scheduling
* purposes and lies dormant until one of two things happens:
*
* <ul>
*
* <li>The lock is acquired by the current thread; or
*
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread.
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
+ * current thread.
*
* </ul>
*
- * <p>If the lock is acquired by the current thread then the lock hold
+ * <p>If the lock is acquired by the current thread then the lock hold
* count is set to one.
*
* <p>If the current thread:
*
* <ul>
*
- * <li>has its interrupted status set on entry to this method; or
+ * <li>has its interrupted status set on entry to this method; or
*
- * <li>is {@link Thread#interrupt interrupted} while acquiring
+ * <li>is {@linkplain Thread#interrupt interrupted} while acquiring
* the lock,
*
* </ul>
*
- * then {@link InterruptedException} is thrown and the current thread's
- * interrupted status is cleared.
+ * then {@link InterruptedException} is thrown and the current thread's
+ * interrupted status is cleared.
*
- * <p>In this implementation, as this method is an explicit interruption
- * point, preference is
- * given to responding to the interrupt over normal or reentrant
- * acquisition of the lock.
+ * <p>In this implementation, as this method is an explicit
+ * interruption point, preference is given to responding to the
+ * interrupt over normal or reentrant acquisition of the lock.
*
* @throws InterruptedException if the current thread is interrupted
*/
- public void lockInterruptibly() throws InterruptedException {
+ public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@@ -308,43 +316,42 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* of invocation.
*
* <p>Acquires the lock if it is not held by another thread and
- * returns immediately with the value <tt>true</tt>, setting the
+ * returns immediately with the value {@code true}, setting the
* lock hold count to one. Even when this lock has been set to use a
- * fair ordering policy, a call to <tt>tryLock()</tt> <em>will</em>
+ * fair ordering policy, a call to {@code tryLock()} <em>will</em>
* immediately acquire the lock if it is available, whether or not
- * other threads are currently waiting for the lock.
- * This &quot;barging&quot; behavior can be useful in certain
+ * other threads are currently waiting for the lock.
+ * This &quot;barging&quot; behavior can be useful in certain
* circumstances, even though it breaks fairness. If you want to honor
- * the fairness setting for this lock, then use
+ * the fairness setting for this lock, then use
* {@link #tryLock(long, TimeUnit) tryLock(0, TimeUnit.SECONDS) }
* which is almost equivalent (it also detects interruption).
*
- * <p> If the current thread
- * already holds this lock then the hold count is incremented by one and
- * the method returns <tt>true</tt>.
+ * <p> If the current thread already holds this lock then the hold
+ * count is incremented by one and the method returns {@code true}.
*
- * <p>If the lock is held by another thread then this method will return
- * immediately with the value <tt>false</tt>.
+ * <p>If the lock is held by another thread then this method will return
+ * immediately with the value {@code false}.
*
- * @return <tt>true</tt> if the lock was free and was acquired by the
- * current thread, or the lock was already held by the current thread; and
- * <tt>false</tt> otherwise.
+ * @return {@code true} if the lock was free and was acquired by the
+ * current thread, or the lock was already held by the current
+ * thread; and {@code false} otherwise
*/
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
/**
- * Acquires the lock if it is not held by another thread within the given
- * waiting time and the current thread has not been
- * {@link Thread#interrupt interrupted}.
+ * Acquires the lock if it is not held by another thread within the given
+ * waiting time and the current thread has not been
+ * {@linkplain Thread#interrupt interrupted}.
*
- * <p>Acquires the lock if it is not held by another thread and returns
- * immediately with the value <tt>true</tt>, setting the lock hold count
+ * <p>Acquires the lock if it is not held by another thread and returns
+ * immediately with the value {@code true}, setting the lock hold count
* to one. If this lock has been set to use a fair ordering policy then
* an available lock <em>will not</em> be acquired if any other threads
* are waiting for the lock. This is in contrast to the {@link #tryLock()}
- * method. If you want a timed <tt>tryLock</tt> that does permit barging on
+ * method. If you want a timed {@code tryLock} that does permit barging on
* a fair lock then combine the timed and un-timed forms together:
*
* <pre>if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... }
@@ -352,60 +359,56 @@ public class ReentrantLock implements Lock, java.io.Serializable {
*
* <p>If the current thread
* already holds this lock then the hold count is incremented by one and
- * the method returns <tt>true</tt>.
+ * the method returns {@code true}.
*
* <p>If the lock is held by another thread then the
- * current thread becomes disabled for thread scheduling
+ * current thread becomes disabled for thread scheduling
* purposes and lies dormant until one of three things happens:
*
* <ul>
*
* <li>The lock is acquired by the current thread; or
*
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread; or
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
+ * the current thread; or
*
* <li>The specified waiting time elapses
*
* </ul>
*
- * <p>If the lock is acquired then the value <tt>true</tt> is returned and
+ * <p>If the lock is acquired then the value {@code true} is returned and
* the lock hold count is set to one.
*
* <p>If the current thread:
*
* <ul>
*
- * <li>has its interrupted status set on entry to this method; or
+ * <li>has its interrupted status set on entry to this method; or
*
- * <li>is {@link Thread#interrupt interrupted} while acquiring
- * the lock,
+ * <li>is {@linkplain Thread#interrupt interrupted} while
+ * acquiring the lock,
*
* </ul>
- * then {@link InterruptedException} is thrown and the current thread's
- * interrupted status is cleared.
+ * then {@link InterruptedException} is thrown and the current thread's
+ * interrupted status is cleared.
*
- * <p>If the specified waiting time elapses then the value <tt>false</tt>
- * is returned.
- * If the time is
- * less than or equal to zero, the method will not wait at all.
+ * <p>If the specified waiting time elapses then the value {@code false}
+ * is returned. If the time is less than or equal to zero, the method
+ * will not wait at all.
*
- * <p>In this implementation, as this method is an explicit interruption
- * point, preference is
- * given to responding to the interrupt over normal or reentrant
- * acquisition of the lock, and over reporting the elapse of the waiting
- * time.
+ * <p>In this implementation, as this method is an explicit
+ * interruption point, preference is given to responding to the
+ * interrupt over normal or reentrant acquisition of the lock, and
+ * over reporting the elapse of the waiting time.
*
* @param timeout the time to wait for the lock
* @param unit the time unit of the timeout argument
- *
- * @return <tt>true</tt> if the lock was free and was acquired by the
- * current thread, or the lock was already held by the current thread; and
- * <tt>false</tt> if the waiting time elapsed before the lock could be
- * acquired.
- *
+ * @return {@code true} if the lock was free and was acquired by the
+ * current thread, or the lock was already held by the current
+ * thread; and {@code false} if the waiting time elapsed before
+ * the lock could be acquired
* @throws InterruptedException if the current thread is interrupted
- * @throws NullPointerException if unit is null
+ * @throws NullPointerException if the time unit is null
*
*/
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
@@ -413,22 +416,22 @@ public class ReentrantLock implements Lock, java.io.Serializable {
}
/**
- * Attempts to release this lock.
+ * Attempts to release this lock.
+ *
+ * <p>If the current thread is the holder of this lock then the hold
+ * count is decremented. If the hold count is now zero then the lock
+ * is released. If the current thread is not the holder of this
+ * lock then {@link IllegalMonitorStateException} is thrown.
*
- * <p>If the current thread is the
- * holder of this lock then the hold count is decremented. If the
- * hold count is now zero then the lock is released. If the
- * current thread is not the holder of this lock then {@link
- * IllegalMonitorStateException} is thrown.
* @throws IllegalMonitorStateException if the current thread does not
- * hold this lock.
+ * hold this lock
*/
public void unlock() {
sync.release(1);
}
/**
- * Returns a {@link Condition} instance for use with this
+ * Returns a {@link Condition} instance for use with this
* {@link Lock} instance.
*
* <p>The returned {@link Condition} instance supports the same
@@ -440,28 +443,28 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* <ul>
*
* <li>If this lock is not held when any of the {@link Condition}
- * {@link Condition#await() waiting} or {@link Condition#signal
- * signalling} methods are called, then an {@link
+ * {@linkplain Condition#await() waiting} or {@linkplain
+ * Condition#signal signalling} methods are called, then an {@link
* IllegalMonitorStateException} is thrown.
*
- * <li>When the condition {@link Condition#await() waiting}
+ * <li>When the condition {@linkplain Condition#await() waiting}
* methods are called the lock is released and, before they
* return, the lock is reacquired and the lock hold count restored
* to what it was when the method was called.
*
- * <li>If a thread is {@link Thread#interrupt interrupted} while
- * waiting then the wait will terminate, an {@link
+ * <li>If a thread is {@linkplain Thread#interrupt interrupted}
+ * while waiting then the wait will terminate, an {@link
* InterruptedException} will be thrown, and the thread's
* interrupted status will be cleared.
*
- * <li> Waiting threads are signalled in FIFO order
+ * <li> Waiting threads are signalled in FIFO order.
*
* <li>The ordering of lock reacquisition for threads returning
* from waiting methods is the same as for threads initially
* acquiring the lock, which is in the default case not specified,
* but for <em>fair</em> locks favors those threads that have been
* waiting the longest.
- *
+ *
* </ul>
*
* @return the Condition object
@@ -473,7 +476,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
/**
* Queries the number of holds on this lock by the current thread.
*
- * <p>A thread has a hold on a lock for each lock action that is not
+ * <p>A thread has a hold on a lock for each lock action that is not
* matched by an unlock action.
*
* <p>The hold count information is typically only used for testing and
@@ -484,8 +487,8 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* <pre>
* class X {
* ReentrantLock lock = new ReentrantLock();
- * // ...
- * public void m() {
+ * // ...
+ * public void m() {
* assert lock.getHoldCount() == 0;
* lock.lock();
* try {
@@ -498,7 +501,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* </pre>
*
* @return the number of holds on this lock by the current thread,
- * or zero if this lock is not held by the current thread.
+ * or zero if this lock is not held by the current thread
*/
public int getHoldCount() {
return sync.getHoldCount();
@@ -517,7 +520,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* ReentrantLock lock = new ReentrantLock();
* // ...
*
- * public void m() {
+ * public void m() {
* assert lock.isHeldByCurrentThread();
* // ... method body
* }
@@ -532,7 +535,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* ReentrantLock lock = new ReentrantLock();
* // ...
*
- * public void m() {
+ * public void m() {
* assert !lock.isHeldByCurrentThread();
* lock.lock();
* try {
@@ -543,8 +546,9 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* }
* }
* </pre>
- * @return <tt>true</tt> if current thread holds this lock and
- * <tt>false</tt> otherwise.
+ *
+ * @return {@code true} if current thread holds this lock and
+ * {@code false} otherwise
*/
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
@@ -552,47 +556,53 @@ public class ReentrantLock implements Lock, java.io.Serializable {
/**
* Queries if this lock is held by any thread. This method is
- * designed for use in monitoring of the system state,
+ * designed for use in monitoring of the system state,
* not for synchronization control.
- * @return <tt>true</tt> if any thread holds this lock and
- * <tt>false</tt> otherwise.
+ *
+ * @return {@code true} if any thread holds this lock and
+ * {@code false} otherwise
*/
public boolean isLocked() {
return sync.isLocked();
}
/**
- * Returns true if this lock has fairness set true.
- * @return true if this lock has fairness set true.
+ * Returns {@code true} if this lock has fairness set true.
+ *
+ * @return {@code true} if this lock has fairness set true
*/
public final boolean isFair() {
return sync instanceof FairSync;
}
/**
- * Returns the thread that currently owns the exclusive lock, or
- * <tt>null</tt> if not owned. Note that the owner may be
- * momentarily <tt>null</tt> even if there are threads trying to
- * acquire the lock but have not yet done so. This method is
- * designed to facilitate construction of subclasses that provide
- * more extensive lock monitoring facilities.
- * @return the owner, or <tt>null</tt> if not owned.
+ * Returns the thread that currently owns this lock, or
+ * {@code null} if not owned. When this method is called by a
+ * thread that is not the owner, the return value reflects a
+ * best-effort approximation of current lock status. For example,
+ * the owner may be momentarily {@code null} even if there are
+ * threads trying to acquire the lock but have not yet done so.
+ * This method is designed to facilitate construction of
+ * subclasses that provide more extensive lock monitoring
+ * facilities.
+ *
+ * @return the owner, or {@code null} if not owned
*/
protected Thread getOwner() {
return sync.getOwner();
}
/**
- * Queries whether any threads are waiting to acquire. Note that
- * because cancellations may occur at any time, a <tt>true</tt>
+ * Queries whether any threads are waiting to acquire this lock. Note that
+ * because cancellations may occur at any time, a {@code true}
* return does not guarantee that any other thread will ever
- * acquire. This method is designed primarily for use in
+ * acquire this lock. This method is designed primarily for use in
* monitoring of the system state.
*
- * @return true if there may be other threads waiting to acquire
- * the lock.
+ * @return {@code true} if there may be other threads waiting to
+ * acquire the lock
*/
- public final boolean hasQueuedThreads() {
+ public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
@@ -600,26 +610,27 @@ public class ReentrantLock implements Lock, java.io.Serializable {
/**
* Queries whether the given thread is waiting to acquire this
* lock. Note that because cancellations may occur at any time, a
- * <tt>true</tt> return does not guarantee that this thread
- * will ever acquire. This method is designed primarily for use
+ * {@code true} return does not guarantee that this thread
+ * will ever acquire this lock. This method is designed primarily for use
* in monitoring of the system state.
*
* @param thread the thread
- * @return true if the given thread is queued waiting for this lock.
- * @throws NullPointerException if thread is null
+ * @return {@code true} if the given thread is queued waiting for this lock
+ * @throws NullPointerException if the thread is null
*/
- public final boolean hasQueuedThread(Thread thread) {
+ public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
/**
* Returns an estimate of the number of threads waiting to
- * acquire. The value is only an estimate because the number of
+ * acquire this lock. The value is only an estimate because the number of
* threads may change dynamically while this method traverses
* internal data structures. This method is designed for use in
* monitoring of the system state, not for synchronization
* control.
+ *
* @return the estimated number of threads waiting for this lock
*/
public final int getQueueLength() {
@@ -628,12 +639,13 @@ public class ReentrantLock implements Lock, java.io.Serializable {
/**
* Returns a collection containing threads that may be waiting to
- * acquire. Because the actual set of threads may change
+ * acquire this lock. Because the actual set of threads may change
* dynamically while constructing this result, the returned
* collection is only a best-effort estimate. The elements of the
* returned collection are in no particular order. This method is
* designed to facilitate construction of subclasses that provide
* more extensive monitoring facilities.
+ *
* @return the collection of threads
*/
protected Collection<Thread> getQueuedThreads() {
@@ -643,18 +655,18 @@ public class ReentrantLock implements Lock, java.io.Serializable {
/**
* Queries whether any threads are waiting on the given condition
* associated with this lock. Note that because timeouts and
- * interrupts may occur at any time, a <tt>true</tt> return does
- * not guarantee that a future <tt>signal</tt> will awaken any
+ * interrupts may occur at any time, a {@code true} return does
+ * not guarantee that a future {@code signal} will awaken any
* threads. This method is designed primarily for use in
* monitoring of the system state.
+ *
* @param condition the condition
- * @return <tt>true</tt> if there are any waiting threads.
- * @throws IllegalMonitorStateException if this lock
- * is not held
+ * @return {@code true} if there are any waiting threads
+ * @throws IllegalMonitorStateException if this lock is not held
* @throws IllegalArgumentException if the given condition is
- * not associated with this lock
- * @throws NullPointerException if condition null
- */
+ * not associated with this lock
+ * @throws NullPointerException if the condition is null
+ */
public boolean hasWaiters(Condition condition) {
if (condition == null)
throw new NullPointerException();
@@ -670,14 +682,14 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* serves only as an upper bound on the actual number of waiters.
* This method is designed for use in monitoring of the system
* state, not for synchronization control.
+ *
* @param condition the condition
- * @return the estimated number of waiting threads.
- * @throws IllegalMonitorStateException if this lock
- * is not held
+ * @return the estimated number of waiting threads
+ * @throws IllegalMonitorStateException if this lock is not held
* @throws IllegalArgumentException if the given condition is
- * not associated with this lock
- * @throws NullPointerException if condition null
- */
+ * not associated with this lock
+ * @throws NullPointerException if the condition is null
+ */
public int getWaitQueueLength(Condition condition) {
if (condition == null)
throw new NullPointerException();
@@ -695,13 +707,13 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* are in no particular order. This method is designed to
* facilitate construction of subclasses that provide more
* extensive condition monitoring facilities.
+ *
* @param condition the condition
* @return the collection of threads
- * @throws IllegalMonitorStateException if this lock
- * is not held
+ * @throws IllegalMonitorStateException if this lock is not held
* @throws IllegalArgumentException if the given condition is
- * not associated with this lock
- * @throws NullPointerException if condition null
+ * not associated with this lock
+ * @throws NullPointerException if the condition is null
*/
protected Collection<Thread> getWaitingThreads(Condition condition) {
if (condition == null)
@@ -712,16 +724,17 @@ public class ReentrantLock implements Lock, java.io.Serializable {
}
/**
- * Returns a string identifying this lock, as well as its lock
- * state. The state, in brackets, includes either the String
- * &quot;Unlocked&quot; or the String &quot;Locked by&quot;
- * followed by the {@link Thread#getName} of the owning thread.
- * @return a string identifying this lock, as well as its lock state.
+ * Returns a string identifying this lock, as well as its lock state.
+ * The state, in brackets, includes either the String {@code "Unlocked"}
+ * or the String {@code "Locked by"} followed by the
+ * {@linkplain Thread#getName name} of the owning thread.
+ *
+ * @return a string identifying this lock, as well as its lock state
*/
public String toString() {
- Thread owner = sync.getOwner();
- return super.toString() + ((owner == null) ?
+ Thread o = sync.getOwner();
+ return super.toString() + ((o == null) ?
"[Unlocked]" :
- "[Locked by thread " + owner.getName() + "]");
+ "[Locked by thread " + o.getName() + "]");
}
}
diff --git a/concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java b/concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java
index 95fc7af..c923944 100644
--- a/concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java
+++ b/concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java
@@ -19,32 +19,60 @@ import java.util.*;
*
* <p> This class does not impose a reader or writer preference
* ordering for lock access. However, it does support an optional
- * <em>fairness</em> policy. When constructed as fair, threads
- * contend for entry using an approximately arrival-order policy. When
- * the write lock is released either the longest-waiting single writer
- * will be assigned the write lock, or if there is a reader waiting
- * longer than any writer, the set of readers will be assigned the
- * read lock. When constructed as non-fair, the order of entry to the
- * lock need not be in arrival order. In either case, if readers are
- * active and a writer enters the lock then no subsequent readers will
- * be granted the read lock until after that writer has acquired and
- * released the write lock.
- *
+ * <em>fairness</em> policy.
+ *
+ * <dl>
+ * <dt><b><i>Non-fair mode (default)</i></b>
+ * <dd>When constructed as non-fair (the default), the order of entry
+ * to the read and write lock is unspecified, subject to reentrancy
+ * constraints. A nonfair lock that is continuously contended may
+ * indefinitely postpone one or more reader or writer threads, but
+ * will normally have higher throughput than a fair lock.
+ * <p>
+ *
+ * <dt><b><i>Fair mode</i></b>
+ * <dd> When constructed as fair, threads contend for entry using an
+ * approximately arrival-order policy. When the currently held lock
+ * is released either the longest-waiting single writer thread will
+ * be assigned the write lock, or if there is a group of reader threads
+ * waiting longer than all waiting writer threads, that group will be
+ * assigned the read lock.
+ *
+ * <p>A thread that tries to acquire a fair read lock (non-reentrantly)
+ * will block if either the write lock is held, or there is a waiting
+ * writer thread. The thread will not acquire the read lock until
+ * after the oldest currently waiting writer thread has acquired and
+ * released the write lock. Of course, if a waiting writer abandons
+ * its wait, leaving one or more reader threads as the longest waiters
+ * in the queue with the write lock free, then those readers will be
+ * assigned the read lock.
+ *
+ * <p>A thread that tries to acquire a fair write lock (non-reentrantly)
+ * will block unless both the read lock and write lock are free (which
+ * implies there are no waiting threads). (Note that the non-blocking
+ * {@link ReadLock#tryLock()} and {@link WriteLock#tryLock()} methods
+ * do not honor this fair setting and will acquire the lock if it is
+ * possible, regardless of waiting threads.)
+ * <p>
+ * </dl>
+ *
* <li><b>Reentrancy</b>
+ *
* <p>This lock allows both readers and writers to reacquire read or
- * write locks in the style of a {@link ReentrantLock}. Readers are not
- * allowed until all write locks held by the writing thread have been
- * released.
- * <p>Additionally, a writer can acquire the read lock - but not vice-versa.
- * Among other applications, reentrancy can be useful when
- * write locks are held during calls or callbacks to methods that
- * perform reads under read locks.
- * If a reader tries to acquire the write lock it will never succeed.
- *
+ * write locks in the style of a {@link ReentrantLock}. Non-reentrant
+ * readers are not allowed until all write locks held by the writing
+ * thread have been released.
+ *
+ * <p>Additionally, a writer can acquire the read lock, but not
+ * vice-versa. Among other applications, reentrancy can be useful
+ * when write locks are held during calls or callbacks to methods that
+ * perform reads under read locks. If a reader tries to acquire the
+ * write lock it will never succeed.
+ *
* <li><b>Lock downgrading</b>
* <p>Reentrancy also allows downgrading from the write lock to a read lock,
* by acquiring the write lock, then the read lock and then releasing the
- * write lock. However, upgrading from a read lock to the write lock, is
+ * write lock. However, upgrading from a read lock to the write lock is
* <b>not</b> possible.
*
* <li><b>Interruption of lock acquisition</b>
@@ -53,96 +81,105 @@ import java.util.*;
*
* <li><b>{@link Condition} support</b>
* <p>The write lock provides a {@link Condition} implementation that
- * behaves in the same way, with respect to the write lock, as the
+ * behaves in the same way, with respect to the write lock, as the
* {@link Condition} implementation provided by
* {@link ReentrantLock#newCondition} does for {@link ReentrantLock}.
* This {@link Condition} can, of course, only be used with the write lock.
+ *
* <p>The read lock does not support a {@link Condition} and
- * <tt>readLock().newCondition()</tt> throws
- * <tt>UnsupportedOperationException</tt>.
+ * {@code readLock().newCondition()} throws
+ * {@code UnsupportedOperationException}.
*
* <li><b>Instrumentation</b>
- * <P> This class supports methods to determine whether locks
+ * <p>This class supports methods to determine whether locks
* are held or contended. These methods are designed for monitoring
* system state, not for synchronization control.
* </ul>
*
- * <p> Serialization of this class behaves in the same way as built-in
+ * <p>Serialization of this class behaves in the same way as built-in
* locks: a deserialized lock is in the unlocked state, regardless of
* its state when serialized.
*
- * <p><b>Sample usages</b>. Here is a code sketch showing how to exploit
- * reentrancy to perform lock downgrading after updating a cache (exception
- * handling is elided for simplicity):
- * <pre>
+ * <p><b>Sample usages</b>. Here is a code sketch showing how to perform
+ * lock downgrading after updating a cache (exception handling is
+ * particularly tricky when handling multiple locks in a non-nested
+ * fashion):
+ *
+ * <pre> {@code
* class CachedData {
* Object data;
* volatile boolean cacheValid;
- * ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
+ * final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
*
* void processCachedData() {
* rwl.readLock().lock();
* if (!cacheValid) {
- * // upgrade lock manually
- * rwl.readLock().unlock(); // must unlock first to obtain writelock
+ * // Must release read lock before acquiring write lock
+ * rwl.readLock().unlock();
* rwl.writeLock().lock();
- * if (!cacheValid) { // recheck
- * data = ...
- * cacheValid = true;
+ * try {
+ * // Recheck state because another thread might have
+ * // acquired write lock and changed state before we did.
+ * if (!cacheValid) {
+ * data = ...
+ * cacheValid = true;
+ * }
+ * // Downgrade by acquiring read lock before releasing write lock
+ * rwl.readLock().lock();
+ * } finally {
+ * rwl.writeLock().unlock(); // Unlock write, still hold read
* }
- * // downgrade lock
- * rwl.readLock().lock(); // reacquire read without giving up write lock
- * rwl.writeLock().unlock(); // unlock write, still hold read
* }
*
- * use(data);
- * rwl.readLock().unlock();
+ * try {
+ * use(data);
+ * } finally {
+ * rwl.readLock().unlock();
+ * }
* }
- * }
- * </pre>
+ * }}</pre>
*
* ReentrantReadWriteLocks can be used to improve concurrency in some
* uses of some kinds of Collections. This is typically worthwhile
* only when the collections are expected to be large, accessed by
* more reader threads than writer threads, and entail operations with
* overhead that outweighs synchronization overhead. For example, here
- * is a class using a TreeMap that is expected to be large and
+ * is a class using a TreeMap that is expected to be large and
* concurrently accessed.
*
- * <pre>
+ * <pre>{@code
* class RWDictionary {
- * private final Map&lt;String, Data&gt; m = new TreeMap&lt;String, Data&gt;();
+ * private final Map<String, Data> m = new TreeMap<String, Data>();
* private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
* private final Lock r = rwl.readLock();
* private final Lock w = rwl.writeLock();
*
* public Data get(String key) {
- * r.lock(); try { return m.get(key); } finally { r.unlock(); }
+ * r.lock();
+ * try { return m.get(key); }
+ * finally { r.unlock(); }
* }
* public String[] allKeys() {
- * r.lock(); try { return m.keySet().toArray(); } finally { r.unlock(); }
+ * r.lock();
+ * try { return m.keySet().toArray(); }
+ * finally { r.unlock(); }
* }
* public Data put(String key, Data value) {
- * w.lock(); try { return m.put(key, value); } finally { w.unlock(); }
+ * w.lock();
+ * try { return m.put(key, value); }
+ * finally { w.unlock(); }
* }
* public void clear() {
- * w.lock(); try { m.clear(); } finally { w.unlock(); }
+ * w.lock();
+ * try { m.clear(); }
+ * finally { w.unlock(); }
* }
- * }
- * </pre>
- *
+ * }}</pre>
*
* <h3>Implementation Notes</h3>
*
- * <p>A reentrant write lock intrinsically defines an owner and can
- * only be released by the thread that acquired it. In contrast, in
- * this implementation, the read lock has no concept of ownership, and
- * there is no requirement that the thread releasing a read lock is
- * the same as the one that acquired it. However, this property is
- * not guaranteed to hold in future implementations of this class.
- *
- * <p> This lock supports a maximum of 65536 recursive write locks
- * and 65536 read locks. Attempts to exceed these limits result in
+ * <p>This lock supports a maximum of 65535 recursive write locks
+ * and 65535 read locks. Attempts to exceed these limits result in
* {@link Error} throws from locking methods.
*
* @since 1.5
@@ -156,26 +193,24 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
/** Inner class providing writelock */
private final ReentrantReadWriteLock.WriteLock writerLock;
/** Performs all synchronization mechanics */
- private final Sync sync;
+ final Sync sync;
/**
- * Creates a new <tt>ReentrantReadWriteLock</tt> with
- * default ordering properties.
+ * Creates a new {@code ReentrantReadWriteLock} with
+ * default (nonfair) ordering properties.
*/
public ReentrantReadWriteLock() {
- sync = new NonfairSync();
- readerLock = new ReadLock(this);
- writerLock = new WriteLock(this);
+ this(false);
}
/**
- * Creates a new <tt>ReentrantReadWriteLock</tt> with
+ * Creates a new {@code ReentrantReadWriteLock} with
* the given fairness policy.
*
- * @param fair true if this lock should use a fair ordering policy
+ * @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantReadWriteLock(boolean fair) {
- sync = (fair)? new FairSync() : new NonfairSync();
+ sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
@@ -183,129 +218,409 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }
- /*
- * Read vs write count extraction constants and functions.
- * Lock state is logically divided into two shorts: The lower
- * one representing the exclusive (writer) lock hold count,
- * and the upper the shared (reader) hold count.
- */
-
- static final int SHARED_SHIFT = 16;
- static final int SHARED_UNIT = (1 << SHARED_SHIFT);
- static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
-
- /** Returns the number of shared holds represented in count */
- static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
- /** Returns the number of exclusive holds represented in count */
- static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
-
- /**
+ /**
* Synchronization implementation for ReentrantReadWriteLock.
* Subclassed into fair and nonfair versions.
*/
- abstract static class Sync extends AbstractQueuedSynchronizer {
- /** Current (exclusive) owner thread */
- transient Thread owner;
+ static abstract class Sync extends AbstractQueuedSynchronizer {
+ private static final long serialVersionUID = 6317671515068378041L;
+
+ /*
+ * Read vs write count extraction constants and functions.
+ * Lock state is logically divided into two unsigned shorts:
+ * The lower one representing the exclusive (writer) lock hold count,
+ * and the upper the shared (reader) hold count.
+ */
+
+ static final int SHARED_SHIFT = 16;
+ static final int SHARED_UNIT = (1 << SHARED_SHIFT);
+ static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
+ static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
+
+ /** Returns the number of shared holds represented in count */
+ static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
+ /** Returns the number of exclusive holds represented in count */
+ static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
+
+ /**
+ * A counter for per-thread read hold counts.
+ * Maintained as a ThreadLocal; cached in cachedHoldCounter
+ */
+ static final class HoldCounter {
+ int count = 0;
+ // Use id, not reference, to avoid garbage retention
+ final long tid = Thread.currentThread().getId();
+ }
+
+ /**
+ * ThreadLocal subclass. Easiest to explicitly define for sake
+ * of deserialization mechanics.
+ */
+ static final class ThreadLocalHoldCounter
+ extends ThreadLocal<HoldCounter> {
+ public HoldCounter initialValue() {
+ return new HoldCounter();
+ }
+ }
+
+ /**
+ * The number of reentrant read locks held by current thread.
+ * Initialized only in constructor and readObject.
+ * Removed whenever a thread's read hold count drops to 0.
+ */
+ private transient ThreadLocalHoldCounter readHolds;
+
+ /**
+ * The hold count of the last thread to successfully acquire
+ * readLock. This saves ThreadLocal lookup in the common case
+ * where the next thread to release is the last one to
+ * acquire. This is non-volatile since it is just used
+ * as a heuristic, and would be great for threads to cache.
+ *
+ * <p>Can outlive the Thread for which it is caching the read
+ * hold count, but avoids garbage retention by not retaining a
+ * reference to the Thread.
+ *
+ * <p>Accessed via a benign data race; relies on the memory
+ * model's final field and out-of-thin-air guarantees.
+ */
+ private transient HoldCounter cachedHoldCounter;
+
+ /**
+ * firstReader is the first thread to have acquired the read lock.
+ * firstReaderHoldCount is firstReader's hold count.
+ *
+ * <p>More precisely, firstReader is the unique thread that last
+ * changed the shared count from 0 to 1, and has not released the
+ * read lock since then; null if there is no such thread.
+ *
+ * <p>Cannot cause garbage retention unless the thread terminated
+ * without relinquishing its read locks, since tryReleaseShared
+ * sets it to null.
+ *
+ * <p>Accessed via a benign data race; relies on the memory
+ * model's out-of-thin-air guarantees for references.
+ *
+ * <p>This allows tracking of read holds for uncontended read
+ * locks to be very cheap.
+ */
+ private transient Thread firstReader = null;
+ private transient int firstReaderHoldCount;
+
+ Sync() {
+ readHolds = new ThreadLocalHoldCounter();
+ setState(getState()); // ensures visibility of readHolds
+ }
+
+ /*
+ * Acquires and releases use the same code for fair and
+ * nonfair locks, but differ in whether/how they allow barging
+ * when queues are non-empty.
+ */
/**
- * Perform write lock. Allows fast path in non-fair version.
+ * Returns true if the current thread, when trying to acquire
+ * the read lock, and otherwise eligible to do so, should block
+ * because of policy for overtaking other waiting threads.
*/
- abstract void wlock();
+ abstract boolean readerShouldBlock();
- /**
- * Perform non-fair tryLock for write. tryAcquire is
- * implemented in subclasses, but both versions need nonfair
- * try for trylock method
+ /**
+ * Returns true if the current thread, when trying to acquire
+ * the write lock, and otherwise eligible to do so, should block
+ * because of policy for overtaking other waiting threads.
*/
- final boolean nonfairTryAcquire(int acquires) {
- // mask out readlocks if called from condition methods
- acquires = exclusiveCount(acquires);
+ abstract boolean writerShouldBlock();
+
+ /*
+ * Note that tryRelease and tryAcquire can be called by
+ * Conditions. So it is possible that their arguments contain
+ * both read and write holds that are all released during a
+ * condition wait and re-established in tryAcquire.
+ */
+
+ protected final boolean tryRelease(int releases) {
+ if (!isHeldExclusively())
+ throw new IllegalMonitorStateException();
+ int nextc = getState() - releases;
+ boolean free = exclusiveCount(nextc) == 0;
+ if (free)
+ setExclusiveOwnerThread(null);
+ setState(nextc);
+ return free;
+ }
+
+ protected final boolean tryAcquire(int acquires) {
+ /*
+ * Walkthrough:
+ * 1. If read count nonzero or write count nonzero
+ * and owner is a different thread, fail.
+ * 2. If count would saturate, fail. (This can only
+ * happen if count is already nonzero.)
+ * 3. Otherwise, this thread is eligible for lock if
+ * it is either a reentrant acquire or
+ * queue policy allows it. If so, update state
+ * and set owner.
+ */
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
- if (w + acquires >= SHARED_UNIT)
- throw new Error("Maximum lock count exceeded");
- if (c != 0 && (w == 0 || current != owner))
- return false;
- if (!compareAndSetState(c, c + acquires))
+ if (c != 0) {
+ // (Note: if c != 0 and w == 0 then shared count != 0)
+ if (w == 0 || current != getExclusiveOwnerThread())
+ return false;
+ if (w + exclusiveCount(acquires) > MAX_COUNT)
+ throw new Error("Maximum lock count exceeded");
+ // Reentrant acquire
+ setState(c + acquires);
+ return true;
+ }
+ if (writerShouldBlock() ||
+ !compareAndSetState(c, c + acquires))
return false;
- owner = current;
+ setExclusiveOwnerThread(current);
return true;
}
- /**
- * Perform nonfair tryLock for read.
+ protected final boolean tryReleaseShared(int unused) {
+ Thread current = Thread.currentThread();
+ if (firstReader == current) {
+ // assert firstReaderHoldCount > 0;
+ if (firstReaderHoldCount == 1)
+ firstReader = null;
+ else
+ firstReaderHoldCount--;
+ } else {
+ HoldCounter rh = cachedHoldCounter;
+ if (rh == null || rh.tid != current.getId())
+ rh = readHolds.get();
+ int count = rh.count;
+ if (count <= 1) {
+ readHolds.remove();
+ if (count <= 0)
+ throw unmatchedUnlockException();
+ }
+ --rh.count;
+ }
+ for (;;) {
+ int c = getState();
+ int nextc = c - SHARED_UNIT;
+ if (compareAndSetState(c, nextc))
+ // Releasing the read lock has no effect on readers,
+ // but it may allow waiting writers to proceed if
+ // both read and write locks are now free.
+ return nextc == 0;
+ }
+ }
+
+ private IllegalMonitorStateException unmatchedUnlockException() {
+ return new IllegalMonitorStateException(
+ "attempt to unlock read lock, not locked by current thread");
+ }
+
+ protected final int tryAcquireShared(int unused) {
+ /*
+ * Walkthrough:
+ * 1. If write lock held by another thread, fail.
+ * 2. Otherwise, this thread is eligible for
+ * lock wrt state, so ask if it should block
+ * because of queue policy. If not, try
+ * to grant by CASing state and updating count.
+ * Note that step does not check for reentrant
+ * acquires, which is postponed to full version
+ * to avoid having to check hold count in
+ * the more typical non-reentrant case.
+ * 3. If step 2 fails either because thread
+ * apparently not eligible or CAS fails or count
+ * saturated, chain to version with full retry loop.
+ */
+ Thread current = Thread.currentThread();
+ int c = getState();
+ if (exclusiveCount(c) != 0 &&
+ getExclusiveOwnerThread() != current)
+ return -1;
+ int r = sharedCount(c);
+ if (!readerShouldBlock() &&
+ r < MAX_COUNT &&
+ compareAndSetState(c, c + SHARED_UNIT)) {
+ if (r == 0) {
+ firstReader = current;
+ firstReaderHoldCount = 1;
+ } else if (firstReader == current) {
+ firstReaderHoldCount++;
+ } else {
+ HoldCounter rh = cachedHoldCounter;
+ if (rh == null || rh.tid != current.getId())
+ cachedHoldCounter = rh = readHolds.get();
+ else if (rh.count == 0)
+ readHolds.set(rh);
+ rh.count++;
+ }
+ return 1;
+ }
+ return fullTryAcquireShared(current);
+ }
+
+ /**
+ * Full version of acquire for reads, that handles CAS misses
+ * and reentrant reads not dealt with in tryAcquireShared.
*/
- final int nonfairTryAcquireShared(int acquires) {
+ final int fullTryAcquireShared(Thread current) {
+ /*
+ * This code is in part redundant with that in
+ * tryAcquireShared but is simpler overall by not
+ * complicating tryAcquireShared with interactions between
+ * retries and lazily reading hold counts.
+ */
+ HoldCounter rh = null;
for (;;) {
int c = getState();
- int nextc = c + (acquires << SHARED_SHIFT);
- if (nextc < c)
+ if (exclusiveCount(c) != 0) {
+ if (getExclusiveOwnerThread() != current)
+ return -1;
+ // else we hold the exclusive lock; blocking here
+ // would cause deadlock.
+ } else if (readerShouldBlock()) {
+ // Make sure we're not acquiring read lock reentrantly
+ if (firstReader == current) {
+ // assert firstReaderHoldCount > 0;
+ } else {
+ if (rh == null) {
+ rh = cachedHoldCounter;
+ if (rh == null || rh.tid != current.getId()) {
+ rh = readHolds.get();
+ if (rh.count == 0)
+ readHolds.remove();
+ }
+ }
+ if (rh.count == 0)
+ return -1;
+ }
+ }
+ if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
- if (exclusiveCount(c) != 0 &&
- owner != Thread.currentThread())
- return -1;
- if (compareAndSetState(c, nextc))
+ if (compareAndSetState(c, c + SHARED_UNIT)) {
+ if (sharedCount(c) == 0) {
+ firstReader = current;
+ firstReaderHoldCount = 1;
+ } else if (firstReader == current) {
+ firstReaderHoldCount++;
+ } else {
+ if (rh == null)
+ rh = cachedHoldCounter;
+ if (rh == null || rh.tid != current.getId())
+ rh = readHolds.get();
+ else if (rh.count == 0)
+ readHolds.set(rh);
+ rh.count++;
+ cachedHoldCounter = rh; // cache for release
+ }
return 1;
- // Recheck count if lost CAS
+ }
}
}
- protected final boolean tryRelease(int releases) {
+ /**
+ * Performs tryLock for write, enabling barging in both modes.
+ * This is identical in effect to tryAcquire except for lack
+ * of calls to writerShouldBlock.
+ */
+ final boolean tryWriteLock() {
Thread current = Thread.currentThread();
int c = getState();
- if (owner != current)
- throw new IllegalMonitorStateException();
- int nextc = c - releases;
- boolean free = false;
- if (exclusiveCount(c) == releases) {
- free = true;
- owner = null;
+ if (c != 0) {
+ int w = exclusiveCount(c);
+ if (w == 0 || current != getExclusiveOwnerThread())
+ return false;
+ if (w == MAX_COUNT)
+ throw new Error("Maximum lock count exceeded");
}
- setState(nextc);
- return free;
+ if (!compareAndSetState(c, c + 1))
+ return false;
+ setExclusiveOwnerThread(current);
+ return true;
}
- protected final boolean tryReleaseShared(int releases) {
+ /**
+ * Performs tryLock for read, enabling barging in both modes.
+ * This is identical in effect to tryAcquireShared except for
+ * lack of calls to readerShouldBlock.
+ */
+ final boolean tryReadLock() {
+ Thread current = Thread.currentThread();
for (;;) {
int c = getState();
- int nextc = c - (releases << SHARED_SHIFT);
- if (nextc < 0)
- throw new IllegalMonitorStateException();
- if (compareAndSetState(c, nextc))
- return nextc == 0;
+ if (exclusiveCount(c) != 0 &&
+ getExclusiveOwnerThread() != current)
+ return false;
+ int r = sharedCount(c);
+ if (r == MAX_COUNT)
+ throw new Error("Maximum lock count exceeded");
+ if (compareAndSetState(c, c + SHARED_UNIT)) {
+ if (r == 0) {
+ firstReader = current;
+ firstReaderHoldCount = 1;
+ } else if (firstReader == current) {
+ firstReaderHoldCount++;
+ } else {
+ HoldCounter rh = cachedHoldCounter;
+ if (rh == null || rh.tid != current.getId())
+ cachedHoldCounter = rh = readHolds.get();
+ else if (rh.count == 0)
+ readHolds.set(rh);
+ rh.count++;
+ }
+ return true;
+ }
}
}
-
+
protected final boolean isHeldExclusively() {
- return exclusiveCount(getState()) != 0 &&
- owner == Thread.currentThread();
+ // While we must in general read state before owner,
+ // we don't need to do so to check if current thread is owner
+ return getExclusiveOwnerThread() == Thread.currentThread();
}
// Methods relayed to outer class
-
- final ConditionObject newCondition() {
- return new ConditionObject();
+
+ final ConditionObject newCondition() {
+ return new ConditionObject();
}
final Thread getOwner() {
- int c = exclusiveCount(getState());
- Thread o = owner;
- return (c == 0)? null : o;
+ // Must read state before owner to ensure memory consistency
+ return ((exclusiveCount(getState()) == 0)?
+ null :
+ getExclusiveOwnerThread());
}
-
+
final int getReadLockCount() {
return sharedCount(getState());
}
-
+
final boolean isWriteLocked() {
return exclusiveCount(getState()) != 0;
}
final int getWriteHoldCount() {
- int c = exclusiveCount(getState());
- Thread o = owner;
- return (o == Thread.currentThread())? c : 0;
+ return isHeldExclusively() ? exclusiveCount(getState()) : 0;
+ }
+
+ final int getReadHoldCount() {
+ if (getReadLockCount() == 0)
+ return 0;
+
+ Thread current = Thread.currentThread();
+ if (firstReader == current)
+ return firstReaderHoldCount;
+
+ HoldCounter rh = cachedHoldCounter;
+ if (rh != null && rh.tid == current.getId())
+ return rh.count;
+
+ int count = readHolds.get().count;
+ if (count == 0) readHolds.remove();
+ return count;
}
/**
@@ -315,78 +630,43 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
+ readHolds = new ThreadLocalHoldCounter();
setState(0); // reset to unlocked state
}
final int getCount() { return getState(); }
}
- /**
+ /**
* Nonfair version of Sync
*/
final static class NonfairSync extends Sync {
- protected final boolean tryAcquire(int acquires) {
- return nonfairTryAcquire(acquires);
+ private static final long serialVersionUID = -8159625535654395037L;
+ final boolean writerShouldBlock() {
+ return false; // writers can always barge
}
-
- protected final int tryAcquireShared(int acquires) {
- return nonfairTryAcquireShared(acquires);
- }
-
- // Use fastpath for main write lock method
- final void wlock() {
- if (compareAndSetState(0, 1))
- owner = Thread.currentThread();
- else
- acquire(1);
+ final boolean readerShouldBlock() {
+ /* As a heuristic to avoid indefinite writer starvation,
+ * block if the thread that momentarily appears to be head
+ * of queue, if one exists, is a waiting writer. This is
+ * only a probabilistic effect since a new reader will not
+ * block if there is a waiting writer behind other enabled
+ * readers that have not yet drained from the queue.
+ */
+ return apparentlyFirstQueuedIsExclusive();
}
}
- /**
+ /**
* Fair version of Sync
*/
final static class FairSync extends Sync {
- protected final boolean tryAcquire(int acquires) {
- // mask out readlocks if called from condition methods
- acquires = exclusiveCount(acquires);
- Thread current = Thread.currentThread();
- Thread first;
- int c = getState();
- int w = exclusiveCount(c);
- if (w + acquires >= SHARED_UNIT)
- throw new Error("Maximum lock count exceeded");
- if ((w == 0 || current != owner) &&
- (c != 0 ||
- ((first = getFirstQueuedThread()) != null &&
- first != current)))
- return false;
- if (!compareAndSetState(c, c + acquires))
- return false;
- owner = current;
- return true;
- }
-
- protected final int tryAcquireShared(int acquires) {
- Thread current = Thread.currentThread();
- for (;;) {
- Thread first = getFirstQueuedThread();
- if (first != null && first != current)
- return -1;
- int c = getState();
- int nextc = c + (acquires << SHARED_SHIFT);
- if (nextc < c)
- throw new Error("Maximum lock count exceeded");
- if (exclusiveCount(c) != 0 &&
- owner != Thread.currentThread())
- return -1;
- if (compareAndSetState(c, nextc))
- return 1;
- // Recheck count if lost CAS
- }
+ private static final long serialVersionUID = -2274990926593161451L;
+ final boolean writerShouldBlock() {
+ return hasQueuedPredecessors();
}
-
- final void wlock() { // no fast path
- acquire(1);
+ final boolean readerShouldBlock() {
+ return hasQueuedPredecessors();
}
}
@@ -396,46 +676,47 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
public static class ReadLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = -5992448646407690164L;
private final Sync sync;
-
- /**
- * Constructor for use by subclasses.
+
+ /**
+ * Constructor for use by subclasses
+ *
* @param lock the outer lock object
- * @throws NullPointerException if lock null
+ * @throws NullPointerException if the lock is null
*/
protected ReadLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
/**
- * Acquires the shared lock.
+ * Acquires the read lock.
*
- * <p>Acquires the lock if it is not held exclusively by
+ * <p>Acquires the read lock if the write lock is not held by
* another thread and returns immediately.
*
- * <p>If the lock is held exclusively by another thread then
+ * <p>If the write lock is held by another thread then
* the current thread becomes disabled for thread scheduling
- * purposes and lies dormant until the lock has been acquired.
+ * purposes and lies dormant until the read lock has been acquired.
*/
- public void lock() {
+ public void lock() {
sync.acquireShared(1);
}
/**
- * Acquires the shared lock unless the current thread is
- * {@link Thread#interrupt interrupted}.
+ * Acquires the read lock unless the current thread is
+ * {@linkplain Thread#interrupt interrupted}.
*
- * <p>Acquires the shared lock if it is not held exclusively
+ * <p>Acquires the read lock if the write lock is not held
* by another thread and returns immediately.
*
- * <p>If the lock is held by another thread then the
- * current thread becomes disabled for thread scheduling
+ * <p>If the write 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:
*
* <ul>
*
- * <li>The lock is acquired by the current thread; or
+ * <li>The read lock is acquired by the current thread; or
*
- * <li>Some other thread {@link Thread#interrupt interrupts}
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread.
*
* </ul>
@@ -444,10 +725,10 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
*
* <ul>
*
- * <li>has its interrupted status set on entry to this method; or
+ * <li>has its interrupted status set on entry to this method; or
*
- * <li>is {@link Thread#interrupt interrupted} while acquiring
- * the lock,
+ * <li>is {@linkplain Thread#interrupt interrupted} while
+ * acquiring the read lock,
*
* </ul>
*
@@ -466,83 +747,83 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
}
/**
- * Acquires the shared lock only if it is not held exclusively by
+ * Acquires the read lock only if the write lock is not held by
* another thread at the time of invocation.
*
- * <p>Acquires the lock if it is not held exclusively by
+ * <p>Acquires the read lock if the write lock is not held by
* another thread and returns immediately with the value
- * <tt>true</tt>. Even when this lock has been set to use a
- * fair ordering policy, a call to <tt>tryLock()</tt>
- * <em>will</em> immediately acquire the lock if it is
+ * {@code true}. Even when this lock has been set to use a
+ * fair ordering policy, a call to {@code tryLock()}
+ * <em>will</em> immediately acquire the read lock if it is
* available, whether or not other threads are currently
- * waiting for the lock. This &quot;barging&quot; behavior
+ * waiting for the read lock. This &quot;barging&quot; behavior
* can be useful in certain circumstances, even though it
* breaks fairness. If you want to honor the fairness setting
* for this lock, then use {@link #tryLock(long, TimeUnit)
* tryLock(0, TimeUnit.SECONDS) } which is almost equivalent
* (it also detects interruption).
*
- * <p>If the lock is held exclusively by another thread then
+ * <p>If the write lock is held by another thread then
* this method will return immediately with the value
- * <tt>false</tt>.
+ * {@code false}.
*
- * @return <tt>true</tt> if the lock was acquired.
+ * @return {@code true} if the read lock was acquired
*/
public boolean tryLock() {
- return sync.nonfairTryAcquireShared(1) >= 0;
+ return sync.tryReadLock();
}
/**
- * Acquires the shared lock if it is not held exclusively by
+ * Acquires the read lock if the write lock is not held by
* another thread within the given waiting time and the
- * current thread has not been {@link Thread#interrupt
+ * current thread has not been {@linkplain Thread#interrupt
* interrupted}.
*
- * <p>Acquires the lock if it is not held exclusively by
+ * <p>Acquires the read lock if the write lock is not held by
* another thread and returns immediately with the value
- * <tt>true</tt>. If this lock has been set to use a fair
+ * {@code true}. If this lock has been set to use a fair
* ordering policy then an available lock <em>will not</em> be
* acquired if any other threads are waiting for the
* lock. This is in contrast to the {@link #tryLock()}
- * method. If you want a timed <tt>tryLock</tt> that does
+ * method. If you want a timed {@code tryLock} that does
* permit barging on a fair lock then combine the timed and
* un-timed forms together:
*
* <pre>if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... }
* </pre>
*
- * <p>If the lock is held exclusively by another thread then the
- * current thread becomes disabled for thread scheduling
+ * <p>If the write lock is held by another thread then the
+ * current thread becomes disabled for thread scheduling
* purposes and lies dormant until one of three things happens:
*
* <ul>
*
- * <li>The lock is acquired by the current thread; or
+ * <li>The read lock is acquired by the current thread; or
*
- * <li>Some other thread {@link Thread#interrupt interrupts} the current
- * thread; or
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
+ * the current thread; or
*
- * <li>The specified waiting time elapses
+ * <li>The specified waiting time elapses.
*
* </ul>
*
- * <p>If the lock is acquired then the value <tt>true</tt> is
+ * <p>If the read lock is acquired then the value {@code true} is
* returned.
*
* <p>If the current thread:
*
* <ul>
*
- * <li>has its interrupted status set on entry to this method; or
+ * <li>has its interrupted status set on entry to this method; or
*
- * <li>is {@link Thread#interrupt interrupted} while acquiring
- * the lock,
+ * <li>is {@linkplain Thread#interrupt interrupted} while
+ * acquiring the read lock,
*
* </ul> then {@link InterruptedException} is thrown and the
* current thread's interrupted status is cleared.
*
* <p>If the specified waiting time elapses then the value
- * <tt>false</tt> is returned. If the time is less than or
+ * {@code false} is returned. If the time is less than or
* equal to zero, the method will not wait at all.
*
* <p>In this implementation, as this method is an explicit
@@ -550,13 +831,11 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
* the interrupt over normal or reentrant acquisition of the
* lock, and over reporting the elapse of the waiting time.
*
- * @param timeout the time to wait for the lock
+ * @param timeout the time to wait for the read lock
* @param unit the time unit of the timeout argument
- *
- * @return <tt>true</tt> if the lock was acquired.
- *
+ * @return {@code true} if the read lock was acquired
* @throws InterruptedException if the current thread is interrupted
- * @throws NullPointerException if unit is null
+ * @throws NullPointerException if the time unit is null
*
*/
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
@@ -564,20 +843,19 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
}
/**
- * Attempts to release this lock.
+ * Attempts to release this lock.
*
* <p> If the number of readers is now zero then the lock
- * is made available for other lock attempts.
+ * is made available for write lock attempts.
*/
public void unlock() {
sync.releaseShared(1);
}
/**
- * Throws UnsupportedOperationException because ReadLocks
- * do not support conditions.
- * @return A new {@link Condition} instance for this <tt>Lock</tt>
- * instance.
+ * Throws {@code UnsupportedOperationException} because
+ * {@code ReadLocks} do not support conditions.
+ *
* @throws UnsupportedOperationException always
*/
public Condition newCondition() {
@@ -586,17 +864,16 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
/**
* Returns a string identifying this lock, as well as its lock state.
- * The state, in brackets, includes the String
- * &quot;Read locks =&quot; followed by the number of held
- * read locks.
- * @return a string identifying this lock, as well as its lock state.
+ * The state, in brackets, includes the String {@code "Read locks ="}
+ * followed by the number of held read locks.
+ *
+ * @return a string identifying this lock, as well as its lock state
*/
public String toString() {
int r = sync.getReadLockCount();
- return super.toString() +
+ return super.toString() +
"[Read locks = " + r + "]";
}
-
}
/**
@@ -605,42 +882,45 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
public static class WriteLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = -4992448646407690164L;
private final Sync sync;
-
- /**
- * Constructor for use by subclasses.
+
+ /**
+ * Constructor for use by subclasses
+ *
* @param lock the outer lock object
- * @throws NullPointerException if lock null
+ * @throws NullPointerException if the lock is null
*/
protected WriteLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
/**
- * Acquire the lock.
+ * Acquires the write lock.
*
- * <p>Acquires the lock if it is not held by another thread
- * and returns immediately, setting the lock hold count to
+ * <p>Acquires the write lock if neither the read nor write lock
+ * are held by another thread
+ * and returns immediately, setting the write lock hold count to
* one.
*
- * <p>If the current thread already holds the lock then the
+ * <p>If the current thread already holds the write lock then the
* hold count is incremented by one and the method returns
* immediately.
*
* <p>If the lock is held by another thread then the current
* thread becomes disabled for thread scheduling purposes and
- * lies dormant until the lock has been acquired, at which
- * time the lock hold count is set to one.
+ * lies dormant until the write lock has been acquired, at which
+ * time the write lock hold count is set to one.
*/
public void lock() {
- sync.wlock();
+ sync.acquire(1);
}
/**
- * Acquires the lock unless the current thread is {@link
- * Thread#interrupt interrupted}.
+ * Acquires the write lock unless the current thread is
+ * {@linkplain Thread#interrupt interrupted}.
*
- * <p>Acquires the lock if it is not held by another thread
- * and returns immediately, setting the lock hold count to
+ * <p>Acquires the write lock if neither the read nor write lock
+ * are held by another thread
+ * and returns immediately, setting the write lock hold count to
* one.
*
* <p>If the current thread already holds this lock then the
@@ -653,14 +933,14 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
*
* <ul>
*
- * <li>The lock is acquired by the current thread; or
+ * <li>The write lock is acquired by the current thread; or
*
- * <li>Some other thread {@link Thread#interrupt interrupts}
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread.
*
* </ul>
*
- * <p>If the lock is acquired by the current thread then the
+ * <p>If the write lock is acquired by the current thread then the
* lock hold count is set to one.
*
* <p>If the current thread:
@@ -670,8 +950,8 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
* <li>has its interrupted status set on entry to this method;
* or
*
- * <li>is {@link Thread#interrupt interrupted} while acquiring
- * the lock,
+ * <li>is {@linkplain Thread#interrupt interrupted} while
+ * acquiring the write lock,
*
* </ul>
*
@@ -690,16 +970,17 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
}
/**
- * Acquires the lock only if it is not held by another thread
+ * Acquires the write lock only if it is not held by another thread
* at the time of invocation.
*
- * <p>Acquires the lock if it is not held by another thread
- * and returns immediately with the value <tt>true</tt>,
- * setting the lock hold count to one. Even when this lock has
+ * <p>Acquires the write lock if neither the read nor write lock
+ * are held by another thread
+ * and returns immediately with the value {@code true},
+ * setting the write lock hold count to one. Even when this lock has
* been set to use a fair ordering policy, a call to
- * <tt>tryLock()</tt> <em>will</em> immediately acquire the
+ * {@code tryLock()} <em>will</em> immediately acquire the
* lock if it is available, whether or not other threads are
- * currently waiting for the lock. This &quot;barging&quot;
+ * currently waiting for the write lock. This &quot;barging&quot;
* behavior can be useful in certain circumstances, even
* though it breaks fairness. If you want to honor the
* fairness setting for this lock, then use {@link
@@ -708,31 +989,32 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
*
* <p> If the current thread already holds this lock then the
* hold count is incremented by one and the method returns
- * <tt>true</tt>.
+ * {@code true}.
*
* <p>If the lock is held by another thread then this method
- * will return immediately with the value <tt>false</tt>.
+ * will return immediately with the value {@code false}.
*
- * @return <tt>true</tt> if the lock was free and was acquired by the
- * current thread, or the lock was already held by the current thread; and
- * <tt>false</tt> otherwise.
+ * @return {@code true} if the lock was free and was acquired
+ * by the current thread, or the write lock was already held
+ * by the current thread; and {@code false} otherwise.
*/
public boolean tryLock( ) {
- return sync.nonfairTryAcquire(1);
+ return sync.tryWriteLock();
}
/**
- * Acquires the lock if it is not held by another thread
+ * Acquires the write lock if it is not held by another thread
* within the given waiting time and the current thread has
- * not been {@link Thread#interrupt interrupted}.
+ * not been {@linkplain Thread#interrupt interrupted}.
*
- * <p>Acquires the lock if it is not held by another thread
- * and returns immediately with the value <tt>true</tt>,
- * setting the lock hold count to one. If this lock has been
+ * <p>Acquires the write lock if neither the read nor write lock
+ * are held by another thread
+ * and returns immediately with the value {@code true},
+ * setting the write lock hold count to one. If this lock has been
* set to use a fair ordering policy then an available lock
* <em>will not</em> be acquired if any other threads are
- * waiting for the lock. This is in contrast to the {@link
- * #tryLock()} method. If you want a timed <tt>tryLock</tt>
+ * waiting for the write lock. This is in contrast to the {@link
+ * #tryLock()} method. If you want a timed {@code tryLock}
* that does permit barging on a fair lock then combine the
* timed and un-timed forms together:
*
@@ -741,7 +1023,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
*
* <p>If the current thread already holds this lock then the
* hold count is incremented by one and the method returns
- * <tt>true</tt>.
+ * {@code true}.
*
* <p>If the lock is held by another thread then the current
* thread becomes disabled for thread scheduling purposes and
@@ -749,17 +1031,17 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
*
* <ul>
*
- * <li>The lock is acquired by the current thread; or
+ * <li>The write lock is acquired by the current thread; or
*
- * <li>Some other thread {@link Thread#interrupt interrupts}
+ * <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread; or
*
* <li>The specified waiting time elapses
*
* </ul>
*
- * <p>If the lock is acquired then the value <tt>true</tt> is
- * returned and the lock hold count is set to one.
+ * <p>If the write lock is acquired then the value {@code true} is
+ * returned and the write lock hold count is set to one.
*
* <p>If the current thread:
*
@@ -768,16 +1050,16 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
* <li>has its interrupted status set on entry to this method;
* or
*
- * <li>is {@link Thread#interrupt interrupted} while acquiring
- * the lock,
+ * <li>is {@linkplain Thread#interrupt interrupted} while
+ * acquiring the write lock,
*
- * </ul>
+ * </ul>
*
* then {@link InterruptedException} is thrown and the current
* thread's interrupted status is cleared.
*
* <p>If the specified waiting time elapses then the value
- * <tt>false</tt> is returned. If the time is less than or
+ * {@code false} is returned. If the time is less than or
* equal to zero, the method will not wait at all.
*
* <p>In this implementation, as this method is an explicit
@@ -785,30 +1067,31 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
* the interrupt over normal or reentrant acquisition of the
* lock, and over reporting the elapse of the waiting time.
*
- * @param timeout the time to wait for the lock
+ * @param timeout the time to wait for the write lock
* @param unit the time unit of the timeout argument
*
- * @return <tt>true</tt> if the lock was free and was acquired
- * by the current thread, or the lock was already held by the
- * current thread; and <tt>false</tt> if the waiting time
+ * @return {@code true} if the lock was free and was acquired
+ * by the current thread, or the write lock was already held by the
+ * current thread; and {@code false} if the waiting time
* elapsed before the lock could be acquired.
*
* @throws InterruptedException if the current thread is interrupted
- * @throws NullPointerException if unit is null
+ * @throws NullPointerException if the time unit is null
*
*/
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
-
+
/**
- * Attempts to release this lock.
+ * Attempts to release this lock.
*
* <p>If the current thread is the holder of this lock then
* the hold count is decremented. If the hold count is now
* zero then the lock is released. If the current thread is
* not the holder of this lock then {@link
* IllegalMonitorStateException} is thrown.
+ *
* @throws IllegalMonitorStateException if the current thread does not
* hold this lock.
*/
@@ -818,7 +1101,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
/**
* Returns a {@link Condition} instance for use with this
- * {@link Lock} instance.
+ * {@link Lock} instance.
* <p>The returned {@link Condition} instance supports the same
* usages as do the {@link Object} monitor methods ({@link
* Object#wait() wait}, {@link Object#notify notify}, and {@link
@@ -834,74 +1117,80 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
* affected. However it is essentially always an error to
* invoke a condition waiting method when the current thread
* has also acquired read locks, since other threads that
- * could unblock it will not be able to access the write
+ * could unblock it will not be able to acquire the write
* lock.)
*
- * <li>When the condition {@link Condition#await() waiting}
+ * <li>When the condition {@linkplain Condition#await() waiting}
* methods are called the write lock is released and, before
* they return, the write lock is reacquired and the lock hold
* count restored to what it was when the method was called.
*
- * <li>If a thread is {@link Thread#interrupt interrupted} while
+ * <li>If a thread is {@linkplain Thread#interrupt interrupted} while
* waiting then the wait will terminate, an {@link
* InterruptedException} will be thrown, and the thread's
* interrupted status will be cleared.
*
- * <li> Waiting threads are signalled in FIFO order
+ * <li> Waiting threads are signalled in FIFO order.
*
* <li>The ordering of lock reacquisition for threads returning
* from waiting methods is the same as for threads initially
* acquiring the lock, which is in the default case not specified,
* but for <em>fair</em> locks favors those threads that have been
* waiting the longest.
- *
+ *
* </ul>
+ *
* @return the Condition object
*/
- public Condition newCondition() {
+ public Condition newCondition() {
return sync.newCondition();
}
/**
* Returns a string identifying this lock, as well as its lock
* state. The state, in brackets includes either the String
- * &quot;Unlocked&quot; or the String &quot;Locked by&quot;
- * followed by the {@link Thread#getName} of the owning thread.
- * @return a string identifying this lock, as well as its lock state.
+ * {@code "Unlocked"} or the String {@code "Locked by"}
+ * followed by the {@linkplain Thread#getName name} of the owning thread.
+ *
+ * @return a string identifying this lock, as well as its lock state
*/
public String toString() {
- Thread owner = sync.getOwner();
- return super.toString() + ((owner == null) ?
+ Thread o = sync.getOwner();
+ return super.toString() + ((o == null) ?
"[Unlocked]" :
- "[Locked by thread " + owner.getName() + "]");
+ "[Locked by thread " + o.getName() + "]");
}
}
-
// Instrumentation and status
/**
- * Returns true if this lock has fairness set true.
- * @return true if this lock has fairness set true.
+ * Returns {@code true} if this lock has fairness set true.
+ *
+ * @return {@code true} if this lock has fairness set true
*/
public final boolean isFair() {
return sync instanceof FairSync;
}
/**
- * Returns the thread that currently owns the exclusive lock, or
- * <tt>null</tt> if not owned. Note that the owner may be
- * momentarily <tt>null</tt> even if there are threads trying to
- * acquire the lock but have not yet done so. This method is
- * designed to facilitate construction of subclasses that provide
- * more extensive lock monitoring facilities.
- * @return the owner, or <tt>null</tt> if not owned.
+ * Returns the thread that currently owns the write lock, or
+ * {@code null} if not owned. When this method is called by a
+ * thread that is not the owner, the return value reflects a
+ * best-effort approximation of current lock status. For example,
+ * the owner may be momentarily {@code null} even if there are
+ * threads trying to acquire the lock but have not yet done so.
+ * This method is designed to facilitate construction of
+ * subclasses that provide more extensive lock monitoring
+ * facilities.
+ *
+ * @return the owner, or {@code null} if not owned
*/
protected Thread getOwner() {
return sync.getOwner();
}
-
+
/**
* Queries the number of read locks held for this lock. This
* method is designed for use in monitoring system state, not for
@@ -916,17 +1205,19 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
* Queries if the write lock is held by any thread. This method is
* designed for use in monitoring system state, not for
* synchronization control.
- * @return <tt>true</tt> if any thread holds write lock and
- * <tt>false</tt> otherwise.
+ *
+ * @return {@code true} if any thread holds the write lock and
+ * {@code false} otherwise
*/
public boolean isWriteLocked() {
return sync.isWriteLocked();
}
/**
- * Queries if the write lock is held by the current thread.
- * @return <tt>true</tt> if current thread holds this lock and
- * <tt>false</tt> otherwise.
+ * Queries if the write lock is held by the current thread.
+ *
+ * @return {@code true} if the current thread holds the write lock and
+ * {@code false} otherwise
*/
public boolean isWriteLockedByCurrentThread() {
return sync.isHeldExclusively();
@@ -937,8 +1228,8 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
* current thread. A writer thread has a hold on a lock for
* each lock action that is not matched by an unlock action.
*
- * @return the number of holds on this lock by the current thread,
- * or zero if this lock is not held by the current thread.
+ * @return the number of holds on the write lock by the current thread,
+ * or zero if the write lock is not held by the current thread
*/
public int getWriteHoldCount() {
return sync.getWriteHoldCount();
@@ -952,6 +1243,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
* returned collection are in no particular order. This method is
* designed to facilitate construction of subclasses that provide
* more extensive lock monitoring facilities.
+ *
* @return the collection of threads
*/
protected Collection<Thread> getQueuedWriterThreads() {
@@ -966,6 +1258,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
* returned collection are in no particular order. This method is
* designed to facilitate construction of subclasses that provide
* more extensive lock monitoring facilities.
+ *
* @return the collection of threads
*/
protected Collection<Thread> getQueuedReaderThreads() {
@@ -973,41 +1266,42 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
}
/**
- * Queries whether any threads are waiting to acquire. Note that
- * because cancellations may occur at any time, a <tt>true</tt>
- * return does not guarantee that any other thread will ever
- * acquire. This method is designed primarily for use in
- * monitoring of the system state.
+ * Queries whether any threads are waiting to acquire the read or
+ * write lock. Note that because cancellations may occur at any
+ * time, a {@code true} return does not guarantee that any other
+ * thread will ever acquire a lock. This method is designed
+ * primarily for use in monitoring of the system state.
*
- * @return true if there may be other threads waiting to acquire
- * the lock.
+ * @return {@code true} if there may be other threads waiting to
+ * acquire the lock
*/
- public final boolean hasQueuedThreads() {
+ public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
/**
- * Queries whether the given thread is waiting to acquire this
- * lock. Note that because cancellations may occur at any time, a
- * <tt>true</tt> return does not guarantee that this thread
- * will ever acquire. This method is designed primarily for use
- * in monitoring of the system state.
+ * Queries whether the given thread is waiting to acquire either
+ * the read or write lock. Note that because cancellations may
+ * occur at any time, a {@code true} return does not guarantee
+ * that this thread will ever acquire a lock. This method is
+ * designed primarily for use in monitoring of the system state.
*
* @param thread the thread
- * @return true if the given thread is queued waiting for this lock.
- * @throws NullPointerException if thread is null
+ * @return {@code true} if the given thread is queued waiting for this lock
+ * @throws NullPointerException if the thread is null
*/
- public final boolean hasQueuedThread(Thread thread) {
+ public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
/**
- * Returns an estimate of the number of threads waiting to
- * acquire. The value is only an estimate because the number of
- * threads may change dynamically while this method traverses
- * internal data structures. This method is designed for use in
- * monitoring of the system state, not for synchronization
- * control.
+ * Returns an estimate of the number of threads waiting to acquire
+ * either the read or write lock. The value is only an estimate
+ * because the number of threads may change dynamically while this
+ * method traverses internal data structures. This method is
+ * designed for use in monitoring of the system state, not for
+ * synchronization control.
+ *
* @return the estimated number of threads waiting for this lock
*/
public final int getQueueLength() {
@@ -1016,12 +1310,13 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
/**
* Returns a collection containing threads that may be waiting to
- * acquire. Because the actual set of threads may change
- * dynamically while constructing this result, the returned
- * collection is only a best-effort estimate. The elements of the
- * returned collection are in no particular order. This method is
- * designed to facilitate construction of subclasses that provide
- * more extensive monitoring facilities.
+ * acquire either the read or write lock. Because the actual set
+ * of threads may change dynamically while constructing this
+ * result, the returned collection is only a best-effort estimate.
+ * The elements of the returned collection are in no particular
+ * order. This method is designed to facilitate construction of
+ * subclasses that provide more extensive monitoring facilities.
+ *
* @return the collection of threads
*/
protected Collection<Thread> getQueuedThreads() {
@@ -1031,18 +1326,18 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
/**
* Queries whether any threads are waiting on the given condition
* associated with the write lock. Note that because timeouts and
- * interrupts may occur at any time, a <tt>true</tt> return does
- * not guarantee that a future <tt>signal</tt> will awaken any
+ * interrupts may occur at any time, a {@code true} return does
+ * not guarantee that a future {@code signal} will awaken any
* threads. This method is designed primarily for use in
* monitoring of the system state.
+ *
* @param condition the condition
- * @return <tt>true</tt> if there are any waiting threads.
- * @throws IllegalMonitorStateException if this lock
- * is not held
+ * @return {@code true} if there are any waiting threads
+ * @throws IllegalMonitorStateException if this lock is not held
* @throws IllegalArgumentException if the given condition is
- * not associated with this lock
- * @throws NullPointerException if condition null
- */
+ * not associated with this lock
+ * @throws NullPointerException if the condition is null
+ */
public boolean hasWaiters(Condition condition) {
if (condition == null)
throw new NullPointerException();
@@ -1058,14 +1353,14 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
* serves only as an upper bound on the actual number of waiters.
* This method is designed for use in monitoring of the system
* state, not for synchronization control.
+ *
* @param condition the condition
- * @return the estimated number of waiting threads.
- * @throws IllegalMonitorStateException if this lock
- * is not held
+ * @return the estimated number of waiting threads
+ * @throws IllegalMonitorStateException if this lock is not held
* @throws IllegalArgumentException if the given condition is
- * not associated with this lock
- * @throws NullPointerException if condition null
- */
+ * not associated with this lock
+ * @throws NullPointerException if the condition is null
+ */
public int getWaitQueueLength(Condition condition) {
if (condition == null)
throw new NullPointerException();
@@ -1083,13 +1378,13 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
* are in no particular order. This method is designed to
* facilitate construction of subclasses that provide more
* extensive condition monitoring facilities.
+ *
* @param condition the condition
* @return the collection of threads
- * @throws IllegalMonitorStateException if this lock
- * is not held
+ * @throws IllegalMonitorStateException if this lock is not held
* @throws IllegalArgumentException if the given condition is
- * not associated with this lock
- * @throws NullPointerException if condition null
+ * not associated with this lock
+ * @throws NullPointerException if the condition is null
*/
protected Collection<Thread> getWaitingThreads(Condition condition) {
if (condition == null)
@@ -1101,18 +1396,19 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
/**
* Returns a string identifying this lock, as well as its lock state.
- * The state, in brackets, includes the String &quot;Write locks =&quot;
- * follwed by the number of reentrantly held write locks, and the
- * String &quot;Read locks =&quot; followed by the number of held
+ * The state, in brackets, includes the String {@code "Write locks ="}
+ * followed by the number of reentrantly held write locks, and the
+ * String {@code "Read locks ="} followed by the number of held
* read locks.
- * @return a string identifying this lock, as well as its lock state.
+ *
+ * @return a string identifying this lock, as well as its lock state
*/
public String toString() {
int c = sync.getCount();
- int w = exclusiveCount(c);
- int r = sharedCount(c);
-
- return super.toString() +
+ int w = Sync.exclusiveCount(c);
+ int r = Sync.sharedCount(c);
+
+ return super.toString() +
"[Write locks = " + w + ", Read locks = " + r + "]";
}
diff --git a/concurrent/src/main/java/java/util/concurrent/locks/package-info.java b/concurrent/src/main/java/java/util/concurrent/locks/package-info.java
new file mode 100644
index 0000000..12560af
--- /dev/null
+++ b/concurrent/src/main/java/java/util/concurrent/locks/package-info.java
@@ -0,0 +1,47 @@
+/*
+ * 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
+ */
+
+/**
+ * Interfaces and classes providing a framework for locking and waiting
+ * for conditions that is distinct from built-in synchronization and
+ * monitors. The framework permits much greater flexibility in the use of
+ * locks and conditions, at the expense of more awkward syntax.
+ *
+ * <p>The {@link java.util.concurrent.locks.Lock} interface supports
+ * locking disciplines that differ in semantics (reentrant, fair, etc),
+ * and that can be used in non-block-structured contexts including
+ * hand-over-hand and lock reordering algorithms. The main implementation
+ * is {@link java.util.concurrent.locks.ReentrantLock}.
+ *
+ * <p>The {@link java.util.concurrent.locks.ReadWriteLock} interface
+ * similarly defines locks that may be shared among readers but are
+ * exclusive to writers. Only a single implementation, {@link
+ * java.util.concurrent.locks.ReentrantReadWriteLock}, is provided, since
+ * it covers most standard usage contexts. But programmers may create
+ * their own implementations to cover nonstandard requirements.
+ *
+ * <p>The {@link java.util.concurrent.locks.Condition} interface
+ * describes condition variables that may be associated with Locks.
+ * These are similar in usage to the implicit monitors accessed using
+ * {@code Object.wait}, but offer extended capabilities.
+ * In particular, multiple {@code Condition} objects may be associated
+ * with a single {@code Lock}. To avoid compatibility issues, the
+ * names of {@code Condition} methods are different from the
+ * corresponding {@code Object} versions.
+ *
+ * <p>The {@link java.util.concurrent.locks.AbstractQueuedSynchronizer}
+ * class serves as a useful superclass for defining locks and other
+ * synchronizers that rely on queuing blocked threads. The {@link
+ * java.util.concurrent.locks.AbstractQueuedLongSynchronizer} class
+ * provides the same functionality but extends support to 64 bits of
+ * synchronization state. The {@link java.util.concurrent.locks.LockSupport}
+ * class provides lower-level blocking and unblocking support that is
+ * useful for those developers implementing their own customized lock
+ * classes.
+ *
+ * @since 1.5
+ */
+package java.util.concurrent.locks;
diff --git a/concurrent/src/main/java/java/util/concurrent/locks/package.html b/concurrent/src/main/java/java/util/concurrent/locks/package.html
deleted file mode 100644
index 1cc156f..0000000
--- a/concurrent/src/main/java/java/util/concurrent/locks/package.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html> <head>
-<title>Locks</title>
-</head>
-
-<body>
-
-Interfaces and classes providing a framework for locking and waiting
-for conditions that is distinct from built-in synchronization and
-monitors. The framework permits much greater flexibility in the use of
-locks and conditions, at the expense of more awkward syntax.
-
-<p> The {@link java.util.concurrent.locks.Lock} interface supports
-locking disciplines that differ in semantics (reentrant, fair, etc),
-and that can be used in non-block-structured contexts including
-hand-over-hand and lock reordering algorithms. The main implementation
-is {@link java.util.concurrent.locks.ReentrantLock}.
-
-<p> The {@link java.util.concurrent.locks.ReadWriteLock} interface
-similarly defines locks that may be shared among readers but are
-exclusive to writers. Only a single implementation, {@link
-java.util.concurrent.locks.ReentrantReadWriteLock}, is provided, since
-it covers most standard usage contexts. But programmers may create
-their own implementations to cover nonstandard requirements.
-
-<p> The {@link java.util.concurrent.locks.Condition} interface
-describes condition variables that may be associated with Locks.
-These are similar in usage to the implicit monitors accessed using
-<tt>Object.wait</tt>, but offer extended capabilities. In particular,
-multiple <tt>Condition</tt> objects may be associated with a single
-<tt>Lock</tt>. To avoid compatibility issues, the names of
-<tt>Condition</tt> methods are different than the corresponding
-<tt>Object</tt> versions.
-
-<p> The {@link java.util.concurrent.locks.AbstractQueuedSynchronizer}
-class serves as a useful superclass for defining locks and other
-synchronizers that rely on queuing blocked threads. The {@link
-java.util.concurrent.locks.LockSupport} class provides lower-level
-blocking and unblocking support that is useful for those developers
-implementing their own customized lock classes.
-
-@since Android 1.0
-
-</body> </html>
diff --git a/concurrent/src/main/java/java/util/concurrent/package-info.java b/concurrent/src/main/java/java/util/concurrent/package-info.java
new file mode 100644
index 0000000..f77e16a
--- /dev/null
+++ b/concurrent/src/main/java/java/util/concurrent/package-info.java
@@ -0,0 +1,233 @@
+/*
+ * 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
+ */
+
+/**
+ * Utility classes commonly useful in concurrent programming. This
+ * package includes a few small standardized extensible frameworks, as
+ * well as some classes that provide useful functionality and are
+ * otherwise tedious or difficult to implement. Here are brief
+ * descriptions of the main components. See also the
+ * {@link java.util.concurrent.locks} and
+ * {@link java.util.concurrent.atomic} packages.
+ *
+ * <h2>Executors</h2>
+ *
+ * <b>Interfaces.</b>
+ *
+ * {@link java.util.concurrent.Executor} is a simple standardized
+ * interface for defining custom thread-like subsystems, including
+ * thread pools, asynchronous IO, and lightweight task frameworks.
+ * Depending on which concrete Executor class is being used, tasks may
+ * execute in a newly created thread, an existing task-execution thread,
+ * or the thread calling {@link java.util.concurrent.Executor#execute
+ * execute}, and may execute sequentially or concurrently.
+ *
+ * {@link java.util.concurrent.ExecutorService} provides a more
+ * complete asynchronous task execution framework. An
+ * ExecutorService manages queuing and scheduling of tasks,
+ * and allows controlled shutdown.
+ *
+ * The {@link java.util.concurrent.ScheduledExecutorService}
+ * subinterface and associated interfaces add support for
+ * delayed and periodic task execution. ExecutorServices
+ * provide methods arranging asynchronous execution of any
+ * function expressed as {@link java.util.concurrent.Callable},
+ * the result-bearing analog of {@link java.lang.Runnable}.
+ *
+ * A {@link java.util.concurrent.Future} returns the results of
+ * a function, allows determination of whether execution has
+ * completed, and provides a means to cancel execution.
+ *
+ * A {@link java.util.concurrent.RunnableFuture} is a {@code Future}
+ * that possesses a {@code run} method that upon execution,
+ * sets its results.
+ *
+ * <p>
+ *
+ * <b>Implementations.</b>
+ *
+ * Classes {@link java.util.concurrent.ThreadPoolExecutor} and
+ * {@link java.util.concurrent.ScheduledThreadPoolExecutor}
+ * provide tunable, flexible thread pools.
+ *
+ * The {@link java.util.concurrent.Executors} class provides
+ * factory methods for the most common kinds and configurations
+ * of Executors, as well as a few utility methods for using
+ * them. Other utilities based on {@code Executors} include the
+ * concrete class {@link java.util.concurrent.FutureTask}
+ * providing a common extensible implementation of Futures, and
+ * {@link java.util.concurrent.ExecutorCompletionService}, that
+ * assists in coordinating the processing of groups of
+ * asynchronous tasks.
+ *
+ * <h2>Queues</h2>
+ *
+ * The {@link java.util.concurrent.ConcurrentLinkedQueue} class
+ * supplies an efficient scalable thread-safe non-blocking FIFO
+ * queue.
+ *
+ * <p>Five implementations in {@code java.util.concurrent} support
+ * the extended {@link java.util.concurrent.BlockingQueue}
+ * interface, that defines blocking versions of put and take:
+ * {@link java.util.concurrent.LinkedBlockingQueue},
+ * {@link java.util.concurrent.ArrayBlockingQueue},
+ * {@link java.util.concurrent.SynchronousQueue},
+ * {@link java.util.concurrent.PriorityBlockingQueue}, and
+ * {@link java.util.concurrent.DelayQueue}.
+ * The different classes cover the most common usage contexts
+ * for producer-consumer, messaging, parallel tasking, and
+ * related concurrent designs.
+ *
+ * <h2>Timing</h2>
+ *
+ * The {@link java.util.concurrent.TimeUnit} class provides
+ * multiple granularities (including nanoseconds) for
+ * specifying and controlling time-out based operations. Most
+ * classes in the package contain operations based on time-outs
+ * in addition to indefinite waits. In all cases that
+ * time-outs are used, the time-out specifies the minimum time
+ * that the method should wait before indicating that it
+ * timed-out. Implementations make a &quot;best effort&quot;
+ * to detect time-outs as soon as possible after they occur.
+ * However, an indefinite amount of time may elapse between a
+ * time-out being detected and a thread actually executing
+ * again after that time-out. All methods that accept timeout
+ * parameters treat values less than or equal to zero to mean
+ * not to wait at all. To wait "forever", you can use a value
+ * of {@code Long.MAX_VALUE}.
+ *
+ * <h2>Synchronizers</h2>
+ *
+ * Four classes aid common special-purpose synchronization idioms.
+ * {@link java.util.concurrent.Semaphore} is a classic concurrency tool.
+ * {@link java.util.concurrent.CountDownLatch} is a very simple yet very
+ * common utility for blocking until a given number of signals, events,
+ * or conditions hold. A {@link java.util.concurrent.CyclicBarrier} is a
+ * resettable multiway synchronization point useful in some styles of
+ * parallel programming. An {@link java.util.concurrent.Exchanger} allows
+ * two threads to exchange objects at a rendezvous point, and is useful
+ * in several pipeline designs.
+ *
+ * <h2>Concurrent Collections</h2>
+ *
+ * Besides Queues, this package supplies Collection implementations
+ * designed for use in multithreaded contexts:
+ * {@link java.util.concurrent.ConcurrentHashMap},
+ * {@link java.util.concurrent.CopyOnWriteArrayList}, and
+ * {@link java.util.concurrent.CopyOnWriteArraySet}.
+ * When many threads are expected to access a given collection, a
+ * {@code ConcurrentHashMap} is normally preferable to a synchronized
+ * {@code HashMap}. A {@code CopyOnWriteArrayList} is preferable to a
+ * synchronized {@code ArrayList} when the expected number of reads and
+ * traversals greatly outnumber the number of updates to a list.
+
+ * <p>The "Concurrent" prefix used with some classes in this package
+ * is a shorthand indicating several differences from similar
+ * "synchronized" classes. For example {@code java.util.Hashtable} and
+ * {@code Collections.synchronizedMap(new HashMap())} are
+ * synchronized. But {@link
+ * java.util.concurrent.ConcurrentHashMap} is "concurrent". A
+ * concurrent collection is thread-safe, but not governed by a
+ * single exclusion lock. In the particular case of
+ * ConcurrentHashMap, it safely permits any number of
+ * concurrent reads as well as a tunable number of concurrent
+ * writes. "Synchronized" classes can be useful when you need
+ * to prevent all access to a collection via a single lock, at
+ * the expense of poorer scalability. In other cases in which
+ * multiple threads are expected to access a common collection,
+ * "concurrent" versions are normally preferable. And
+ * unsynchronized collections are preferable when either
+ * collections are unshared, or are accessible only when
+ * holding other locks.
+ *
+ * <p>Most concurrent Collection implementations (including most
+ * Queues) also differ from the usual java.util conventions in that
+ * their Iterators provide <em>weakly consistent</em> rather than
+ * fast-fail traversal. A weakly consistent iterator is thread-safe,
+ * but does not necessarily freeze the collection while iterating, so
+ * it may (or may not) reflect any updates since the iterator was
+ * created.
+ *
+ * <h2><a name="MemoryVisibility">Memory Consistency Properties</a></h2>
+ *
+ * <a href="http://java.sun.com/docs/books/jls/third_edition/html/memory.html">
+ * Chapter 17 of the Java Language Specification</a> defines the
+ * <i>happens-before</i> relation on memory operations such as reads and
+ * writes of shared variables. The results of a write by one thread are
+ * guaranteed to be visible to a read by another thread only if the write
+ * operation <i>happens-before</i> the read operation. The
+ * {@code synchronized} and {@code volatile} constructs, as well as the
+ * {@code Thread.start()} and {@code Thread.join()} methods, can form
+ * <i>happens-before</i> relationships. In particular:
+ *
+ * <ul>
+ * <li>Each action in a thread <i>happens-before</i> every action in that
+ * thread that comes later in the program's order.
+ *
+ * <li>An unlock ({@code synchronized} block or method exit) of a
+ * monitor <i>happens-before</i> every subsequent lock ({@code synchronized}
+ * block or method entry) of that same monitor. And because
+ * the <i>happens-before</i> relation is transitive, all actions
+ * of a thread prior to unlocking <i>happen-before</i> all actions
+ * subsequent to any thread locking that monitor.
+ *
+ * <li>A write to a {@code volatile} field <i>happens-before</i> every
+ * subsequent read of that same field. Writes and reads of
+ * {@code volatile} fields have similar memory consistency effects
+ * as entering and exiting monitors, but do <em>not</em> entail
+ * mutual exclusion locking.
+ *
+ * <li>A call to {@code start} on a thread <i>happens-before</i> any
+ * action in the started thread.
+ *
+ * <li>All actions in a thread <i>happen-before</i> any other thread
+ * successfully returns from a {@code join} on that thread.
+ *
+ * </ul>
+ *
+ *
+ * The methods of all classes in {@code java.util.concurrent} and its
+ * subpackages extend these guarantees to higher-level
+ * synchronization. In particular:
+ *
+ * <ul>
+ *
+ * <li>Actions in a thread prior to placing an object into any concurrent
+ * collection <i>happen-before</i> actions subsequent to the access or
+ * removal of that element from the collection in another thread.
+ *
+ * <li>Actions in a thread prior to the submission of a {@code Runnable}
+ * to an {@code Executor} <i>happen-before</i> its execution begins.
+ * Similarly for {@code Callables} submitted to an {@code ExecutorService}.
+ *
+ * <li>Actions taken by the asynchronous computation represented by a
+ * {@code Future} <i>happen-before</i> actions subsequent to the
+ * retrieval of the result via {@code Future.get()} in another thread.
+ *
+ * <li>Actions prior to "releasing" synchronizer methods such as
+ * {@code Lock.unlock}, {@code Semaphore.release}, and
+ * {@code CountDownLatch.countDown} <i>happen-before</i> actions
+ * subsequent to a successful "acquiring" method such as
+ * {@code Lock.lock}, {@code Semaphore.acquire},
+ * {@code Condition.await}, and {@code CountDownLatch.await} on the
+ * same synchronizer object in another thread.
+ *
+ * <li>For each pair of threads that successfully exchange objects via
+ * an {@code Exchanger}, actions prior to the {@code exchange()}
+ * in each thread <i>happen-before</i> those subsequent to the
+ * corresponding {@code exchange()} in another thread.
+ *
+ * <li>Actions prior to calling {@code CyclicBarrier.await}
+ * <i>happen-before</i> actions performed by the barrier action, and
+ * actions performed by the barrier action <i>happen-before</i> actions
+ * subsequent to a successful return from the corresponding {@code await}
+ * in other threads.
+ *
+ * </ul>
+ *
+ * @since 1.5
+ */
+package java.util.concurrent;
diff --git a/concurrent/src/main/java/java/util/concurrent/package.html b/concurrent/src/main/java/java/util/concurrent/package.html
deleted file mode 100644
index a5bb8d2..0000000
--- a/concurrent/src/main/java/java/util/concurrent/package.html
+++ /dev/null
@@ -1,125 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html> <head>
-<title>Concurrency Utilities</title>
-</head>
-
-<body>
-
-<p> Utility classes commonly useful in concurrent programming. This
-package includes a few small standardized extensible frameworks, as
-well as some classes that provide useful functionality and are
-otherwise tedious or difficult to implement. Here are brief
-descriptions of the main components. See also the <tt>locks</tt> and
-<tt>atomic</tt> packages.
-
-<h2>Executors</h2>
-
-<b>Interfaces.</b> {@link java.util.concurrent.Executor} is a simple
-standardized interface for defining custom thread-like subsystems,
-including thread pools, asynchronous IO, and lightweight task
-frameworks. Depending on which concrete Executor class is being used,
-tasks may execute in a newly created thread, an existing
-task-execution thread, or the thread calling <tt>execute()</tt>, and
-may execute sequentially or concurrently. {@link
-java.util.concurrent.ExecutorService} provides a more complete
-asynchronous task execution framework. An ExecutorService manages
-queuing and scheduling of tasks, and allows controlled shutdown. The
-{@link java.util.concurrent.ScheduledExecutorService} subinterface
-adds support for delayed and periodic task execution.
-ExecutorServices provide methods arranging asynchronous execution of
-any function expressed as {@link java.util.concurrent.Callable}, the
-result-bearing analog of {@link java.lang.Runnable}. A {@link
-java.util.concurrent.Future} returns the results of a function, allows
-determination of whether execution has completed, and provides a means to
-cancel execution.
-
-<p>
-
-<b>Implementations.</b> Classes {@link
-java.util.concurrent.ThreadPoolExecutor} and {@link
-java.util.concurrent.ScheduledThreadPoolExecutor} provide tunable,
-flexible thread pools. The {@link java.util.concurrent.Executors}
-class provides factory methods for the most common kinds and
-configurations of Executors, as well as a few utility methods for
-using them. Other utilities based on Executors include the concrete
-class {@link java.util.concurrent.FutureTask} providing a common
-extensible implementation of Futures, and {@link
-java.util.concurrent.ExecutorCompletionService}, that assists in
-coordinating the processing of groups of asynchronous tasks.
-
-<h2>Queues</h2>
-
-The java.util.concurrent {@link
-java.util.concurrent.ConcurrentLinkedQueue} class supplies an
-efficient scalable thread-safe non-blocking FIFO queue. Five
-implementations in java.util.concurrent support the extended {@link
-java.util.concurrent.BlockingQueue} interface, that defines blocking
-versions of put and take: {@link
-java.util.concurrent.LinkedBlockingQueue}, {@link
-java.util.concurrent.ArrayBlockingQueue}, {@link
-java.util.concurrent.SynchronousQueue}, {@link
-java.util.concurrent.PriorityBlockingQueue}, and {@link
-java.util.concurrent.DelayQueue}. The different classes cover the most
-common usage contexts for producer-consumer, messaging, parallel
-tasking, and related concurrent designs.
-
-
-<h2>Timing</h2>
-
-The {@link java.util.concurrent.TimeUnit} class provides multiple
-granularities (including nanoseconds) for specifying and controlling
-time-out based operations. Most classes in the package contain
-operations based on time-outs in addition to indefinite waits. In all
-cases that time-outs are used, the time-out specifies the minimum time
-that the method should wait before indicating that it
-timed-out. Implementations make a &quot;best effort&quot; to detect
-time-outs as soon as possible after they occur. However, an indefinite
-amount of time may elapse between a time-out being detected and a
-thread actually executing again after that time-out.
-
-<h2>Synchronizers</h2>
-
-Four classes aid common special-purpose synchronization idioms.
-{@link java.util.concurrent.Semaphore} is a classic concurrency tool.
-{@link java.util.concurrent.CountDownLatch} is a very simple yet very
-common utility for blocking until a given number of signals, events,
-or conditions hold. A {@link java.util.concurrent.CyclicBarrier} is a
-resettable multiway synchronization point useful in some styles of
-parallel programming. An {@link java.util.concurrent.Exchanger} allows
-two threads to exchange objects at a rendezvous point, and is useful
-in several pipeline designs.
-
-<h2>Concurrent Collections</h2>
-
-Besides Queues, this package supplies a few Collection implementations
-designed for use in multithreaded contexts: {@link
-java.util.concurrent.ConcurrentHashMap}, {@link
-java.util.concurrent.CopyOnWriteArrayList}, and {@link
-java.util.concurrent.CopyOnWriteArraySet}.
-
-<p>The "Concurrent" prefix used with some classes in this package is a
-shorthand indicating several differences from similar "synchronized"
-classes. For example <tt>java.util.Hashtable</tt> and
-<tt>Collections.synchronizedMap(new HashMap())</tt> are
-synchronized. But {@link java.util.concurrent.ConcurrentHashMap} is
-"concurrent". A concurrent collection is thread-safe, but not
-governed by a single exclusion lock. In the particular case of
-ConcurrentHashMap, it safely permits any number of concurrent reads as
-well as a tunable number of concurrent writes. "Synchronized" classes
-can be useful when you need to prevent all access to a collection via
-a single lock, at the expense of poorer scalability. In other cases in
-which multiple threads are expected to access a common collection,
-"concurrent" versions are normally preferable. And unsynchronized
-collections are preferable when either collections are unshared, or
-are accessible only when holding other locks.
-
-<p> Most concurrent Collection implementations (including most Queues)
-also differ from the usual java.util conventions in that their Iterators
-provide <em>weakly consistent</em> rather than fast-fail traversal. A
-weakly consistent iterator is thread-safe, but does not necessarily
-freeze the collection while iterating, so it may (or may not) reflect
-any updates since the iterator was created.
-
-@since Android 1.0
-
-</body> </html>
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/AbstractQueuedSynchronizerTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/AbstractQueuedSynchronizerTest.java
index 7102c14..db89645 100644
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/AbstractQueuedSynchronizerTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/AbstractQueuedSynchronizerTest.java
@@ -462,7 +462,9 @@ public class AbstractQueuedSynchronizerTest extends JSR166TestCase {
Thread t = new Thread(new InterruptedSyncRunnable(sync));
try {
t.start();
+ Thread.sleep(SHORT_DELAY_MS);
t.interrupt();
+ Thread.sleep(SHORT_DELAY_MS);
sync.release(1);
t.join();
} catch(Exception e){
@@ -951,7 +953,6 @@ public class AbstractQueuedSynchronizerTest extends JSR166TestCase {
sync.acquire(1);
c.signal();
sync.release(1);
- assert(t.isInterrupted());
t.join(SHORT_DELAY_MS);
assertFalse(t.isAlive());
}
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/ArrayBlockingQueueTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/ArrayBlockingQueueTest.java
index da6988f..b650ede 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/ArrayBlockingQueueTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/ArrayBlockingQueueTest.java
@@ -651,6 +651,7 @@ public class ArrayBlockingQueueTest extends JSR166TestCase {
assertEquals(SIZE, q.remainingCapacity());
q.add(one);
assertFalse(q.isEmpty());
+ assertTrue(q.contains(one));
q.clear();
assertTrue(q.isEmpty());
}
@@ -971,6 +972,17 @@ public class ArrayBlockingQueueTest extends JSR166TestCase {
assertEquals(l.size(), SIZE);
for (int i = 0; i < SIZE; ++i)
assertEquals(l.get(i), new Integer(i));
+ q.add(zero);
+ q.add(one);
+ assertFalse(q.isEmpty());
+ assertTrue(q.contains(zero));
+ assertTrue(q.contains(one));
+ l.clear();
+ q.drainTo(l);
+ assertEquals(q.size(), 0);
+ assertEquals(l.size(), 2);
+ for (int i = 0; i < 2; ++i)
+ assertEquals(l.get(i), new Integer(i));
}
/**
@@ -1029,15 +1041,18 @@ public class ArrayBlockingQueueTest extends JSR166TestCase {
* drainTo(c, n) empties first max {n, size} elements of queue into c
*/
public void testDrainToN() {
+ ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE*2);
for (int i = 0; i < SIZE + 2; ++i) {
- ArrayBlockingQueue q = populatedQueue(SIZE);
+ for(int j = 0; j < SIZE; j++)
+ assertTrue(q.offer(new Integer(j)));
ArrayList l = new ArrayList();
q.drainTo(l, i);
int k = (i < SIZE)? i : SIZE;
- assertEquals(q.size(), SIZE-k);
assertEquals(l.size(), k);
+ assertEquals(q.size(), SIZE-k);
for (int j = 0; j < k; ++j)
assertEquals(l.get(j), new Integer(j));
+ while (q.poll() != null) ;
}
}
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerFieldUpdaterTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerFieldUpdaterTest.java
index 03fd2c8..4c3ecb4 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerFieldUpdaterTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerFieldUpdaterTest.java
@@ -62,6 +62,27 @@ public class AtomicIntegerFieldUpdaterTest extends JSR166TestCase {
catch (RuntimeException rt) {}
}
+ static class Base {
+ protected volatile int f = 0;
+ }
+ static class Sub1 extends Base {
+ AtomicIntegerFieldUpdater<Base> fUpdater
+ = AtomicIntegerFieldUpdater.newUpdater(Base.class, "f");
+ }
+ static class Sub2 extends Base {}
+
+ public void testProtectedFieldOnAnotherSubtype() {
+ Sub1 sub1 = new Sub1();
+ Sub2 sub2 = new Sub2();
+
+ sub1.fUpdater.set(sub1, 1);
+ try {
+ sub1.fUpdater.set(sub2, 2);
+ shouldThrow();
+ }
+ catch (RuntimeException rt) {}
+ }
+
/**
* get returns the last value set or assigned
*/
@@ -80,6 +101,7 @@ public class AtomicIntegerFieldUpdaterTest extends JSR166TestCase {
assertEquals(-3,a.get(this));
}
+
/**
* compareAndSet succeeds in changing value if equal to expected else fails
*/
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerTest.java
index 1771d80..d484785 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerTest.java
@@ -48,6 +48,7 @@ public class AtomicIntegerTest extends JSR166TestCase {
assertEquals(-3,ai.get());
}
+
/**
* compareAndSet succeeds in changing value if equal to expected else fails
*/
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongFieldUpdaterTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongFieldUpdaterTest.java
index 8076f7b..9310795 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongFieldUpdaterTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongFieldUpdaterTest.java
@@ -29,7 +29,7 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase {
*/
public void testConstructor(){
try{
- AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest>
+ AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest>
a = AtomicLongFieldUpdater.newUpdater
(AtomicLongFieldUpdaterTest.class, "y");
shouldThrow();
@@ -42,7 +42,7 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase {
*/
public void testConstructor2(){
try{
- AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest>
+ AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest>
a = AtomicLongFieldUpdater.newUpdater
(AtomicLongFieldUpdaterTest.class, "z");
shouldThrow();
@@ -55,7 +55,7 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase {
*/
public void testConstructor3(){
try{
- AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest>
+ AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest>
a = AtomicLongFieldUpdater.newUpdater
(AtomicLongFieldUpdaterTest.class, "w");
shouldThrow();
@@ -64,6 +64,27 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase {
catch (RuntimeException rt) {}
}
+ static class Base {
+ protected volatile long f = 0;
+ }
+ static class Sub1 extends Base {
+ AtomicLongFieldUpdater<Base> fUpdater
+ = AtomicLongFieldUpdater.newUpdater(Base.class, "f");
+ }
+ static class Sub2 extends Base {}
+
+ public void testProtectedFieldOnAnotherSubtype() {
+ Sub1 sub1 = new Sub1();
+ Sub2 sub2 = new Sub2();
+
+ sub1.fUpdater.set(sub1, 1);
+ try {
+ sub1.fUpdater.set(sub2, 2);
+ shouldThrow();
+ }
+ catch (RuntimeException rt) {}
+ }
+
/**
* get returns the last value set or assigned
*/
@@ -75,12 +96,12 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase {
return;
}
x = 1;
- assertEquals(1,a.get(this));
- a.set(this,2);
- assertEquals(2,a.get(this));
- a.set(this,-3);
- assertEquals(-3,a.get(this));
-
+ assertEquals(1,a.get(this));
+ a.set(this,2);
+ assertEquals(2,a.get(this));
+ a.set(this,-3);
+ assertEquals(-3,a.get(this));
+
}
/**
* compareAndSet succeeds in changing value if equal to expected else fails
@@ -134,7 +155,7 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase {
/**
* repeated weakCompareAndSet succeeds in changing value when equal
- * to expected
+ * to expected
*/
public void testWeakCompareAndSet(){
AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongTest.java
index 55b8a49..143c84a 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongTest.java
@@ -48,6 +48,7 @@ public class AtomicLongTest extends JSR166TestCase {
assertEquals(-3,ai.get());
}
+
/**
* compareAndSet succeeds in changing value if equal to expected else fails
*/
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceFieldUpdaterTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceFieldUpdaterTest.java
index 183ca21..feddce7 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceFieldUpdaterTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceFieldUpdaterTest.java
@@ -64,6 +64,27 @@ public class AtomicReferenceFieldUpdaterTest extends JSR166TestCase{
catch (RuntimeException rt) {}
}
+ static class Base {
+ protected volatile Object f = null;
+ }
+ static class Sub1 extends Base {
+ AtomicReferenceFieldUpdater<Base, Object> fUpdater
+ = AtomicReferenceFieldUpdater.newUpdater(Base.class, Object.class, "f");
+ }
+ static class Sub2 extends Base {}
+
+ public void testProtectedFieldOnAnotherSubtype() {
+ Sub1 sub1 = new Sub1();
+ Sub2 sub2 = new Sub2();
+
+ sub1.fUpdater.set(sub1, "f");
+ try {
+ sub1.fUpdater.set(sub2, "g");
+ shouldThrow();
+ }
+ catch (RuntimeException rt) {}
+ }
+
/**
* get returns the last value set or assigned
*/
@@ -75,14 +96,11 @@ public class AtomicReferenceFieldUpdaterTest extends JSR166TestCase{
return;
}
x = one;
- assertEquals(one,a.get(this));
- a.set(this,two);
- assertEquals(two,a.get(this));
- a.set(this,-3);
- // BEGIN android-changed
- assertEquals(new Integer(-3),a.get(this));
- // END android-changed
-
+ assertEquals(one,a.get(this));
+ a.set(this,two);
+ assertEquals(two,a.get(this));
+ a.set(this,m3);
+ assertEquals(m3,a.get(this));
}
/**
* compareAndSet succeeds in changing value if equal to expected else fails
@@ -135,7 +153,7 @@ public class AtomicReferenceFieldUpdaterTest extends JSR166TestCase{
/**
* repeated weakCompareAndSet succeeds in changing value when equal
- * to expected
+ * to expected
*/
public void testWeakCompareAndSet(){
AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer>a;
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceTest.java
index 0aa48cd..33da30d 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceTest.java
@@ -46,7 +46,7 @@ public class AtomicReferenceTest extends JSR166TestCase {
assertEquals(two,ai.get());
ai.set(m3);
assertEquals(m3,ai.get());
-
+
}
/**
* compareAndSet succeeds in changing value if equal to expected else fails
@@ -86,7 +86,7 @@ public class AtomicReferenceTest extends JSR166TestCase {
/**
* repeated weakCompareAndSet succeeds in changing value when equal
- * to expected
+ * to expected
*/
public void testWeakCompareAndSet(){
AtomicReference ai = new AtomicReference(one);
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/ConcurrentHashMapTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/ConcurrentHashMapTest.java
index 9b3ec52..d7f2210 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/ConcurrentHashMapTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/ConcurrentHashMapTest.java
@@ -16,59 +16,16 @@ import java.io.*;
public class ConcurrentHashMapTest extends JSR166TestCase{
public static void main(String[] args) {
- junit.textui.TestRunner.run (suite());
+ junit.textui.TestRunner.run (suite());
}
public static Test suite() {
return new TestSuite(ConcurrentHashMapTest.class);
}
- // BEGIN android-added
- static class MyConcurrentHashMap<V, K> extends ConcurrentHashMap<K, V>
- implements Cloneable {
-
- public MyConcurrentHashMap() {
- super();
- }
-
- public MyConcurrentHashMap(int initialCapacity, float loadFactor,
- int concurrencyLevel) {
- super(initialCapacity, loadFactor, concurrencyLevel);
- }
-
- public MyConcurrentHashMap(int initialCapacity) {
- super(initialCapacity);
- }
-
- public MyConcurrentHashMap(Map<? extends K, ? extends V> t) {
- super(t);
- }
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
-
- }
/**
* Create a map from Integers 1-5 to Strings "A"-"E".
*/
- private static MyConcurrentHashMap myMap5() {
- MyConcurrentHashMap map = new MyConcurrentHashMap(5);
- assertTrue(map.isEmpty());
- map.put(one, "A");
- map.put(two, "B");
- map.put(three, "C");
- map.put(four, "D");
- map.put(five, "E");
- assertFalse(map.isEmpty());
- assertEquals(5, map.size());
- return map;
- }
- // END android-added
- /**
- * Create a map from Integers 1-5 to Strings "A"-"E".
- */
- private static ConcurrentHashMap map5() {
+ private static ConcurrentHashMap map5() {
ConcurrentHashMap map = new ConcurrentHashMap(5);
assertTrue(map.isEmpty());
map.put(one, "A");
@@ -111,7 +68,7 @@ public class ConcurrentHashMapTest extends JSR166TestCase{
assertTrue(map.contains("A"));
assertFalse(map.contains("Z"));
}
-
+
/**
* containsKey returns true for contained key
*/
@@ -126,8 +83,8 @@ public class ConcurrentHashMapTest extends JSR166TestCase{
*/
public void testContainsValue() {
ConcurrentHashMap map = map5();
- assertTrue(map.contains("A"));
- assertFalse(map.contains("Z"));
+ assertTrue(map.containsValue("A"));
+ assertFalse(map.containsValue("Z"));
}
/**
@@ -146,21 +103,6 @@ public class ConcurrentHashMapTest extends JSR166TestCase{
}
/**
- * Clone creates an equal map
- */
- public void testClone() {
- // BEGIN android-changed
- MyConcurrentHashMap map =myMap5();
- try {
- MyConcurrentHashMap m2 = (MyConcurrentHashMap)(map.clone());
- assertEquals(map, m2);
- } catch (CloneNotSupportedException e) {
- fail("clone not supported");
- }
- // END android-changed
- }
-
- /**
* get returns the correct element at the given key,
* or null if not present
*/
@@ -210,6 +152,49 @@ public class ConcurrentHashMapTest extends JSR166TestCase{
}
/**
+ * keySet.toArray returns contains all keys
+ */
+ public void testKeySetToArray() {
+ ConcurrentHashMap map = map5();
+ Set s = map.keySet();
+ Object[] ar = s.toArray();
+ assertTrue(s.containsAll(Arrays.asList(ar)));
+ assertEquals(5, ar.length);
+ ar[0] = m10;
+ assertFalse(s.containsAll(Arrays.asList(ar)));
+ }
+
+ /**
+ * Values.toArray contains all values
+ */
+ public void testValuesToArray() {
+ ConcurrentHashMap map = map5();
+ Collection v = map.values();
+ Object[] ar = v.toArray();
+ ArrayList s = new ArrayList(Arrays.asList(ar));
+ assertEquals(5, ar.length);
+ assertTrue(s.contains("A"));
+ assertTrue(s.contains("B"));
+ assertTrue(s.contains("C"));
+ assertTrue(s.contains("D"));
+ assertTrue(s.contains("E"));
+ }
+
+ /**
+ * entrySet.toArray contains all entries
+ */
+ public void testEntrySetToArray() {
+ ConcurrentHashMap map = map5();
+ Set s = map.entrySet();
+ Object[] ar = s.toArray();
+ assertEquals(5, ar.length);
+ for (int i = 0; i < 5; ++i) {
+ assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
+ assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
+ }
+ }
+
+ /**
* values collection contains all values
*/
public void testValues() {
@@ -233,7 +218,7 @@ public class ConcurrentHashMapTest extends JSR166TestCase{
Iterator it = s.iterator();
while (it.hasNext()) {
Map.Entry e = (Map.Entry) it.next();
- assertTrue(
+ assertTrue(
(e.getKey().equals(one) && e.getValue().equals("A")) ||
(e.getKey().equals(two) && e.getValue().equals("B")) ||
(e.getKey().equals(three) && e.getValue().equals("C")) ||
@@ -357,12 +342,12 @@ public class ConcurrentHashMapTest extends JSR166TestCase{
for (int i = 1; i <= 5; ++i) {
assertTrue(s.indexOf(String.valueOf(i)) >= 0);
}
- }
+ }
// Exception tests
-
+
/**
- * Cannot create with negative capacity
+ * Cannot create with negative capacity
*/
public void testConstructor1() {
try {
@@ -561,6 +546,19 @@ public class ConcurrentHashMapTest extends JSR166TestCase{
}
/**
+ * remove(x, null) returns false
+ */
+ public void testRemove3() {
+ try {
+ ConcurrentHashMap c = new ConcurrentHashMap(5);
+ c.put("sadsdf", "asdads");
+ assertFalse(c.remove("sadsdf", null));
+ } catch(NullPointerException e){
+ fail();
+ }
+ }
+
+ /**
* A deserialized map equals original
*/
public void testSerialization() {
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/CyclicBarrierTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/CyclicBarrierTest.java
index 9eac1a7..ecd6e45 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/CyclicBarrierTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/CyclicBarrierTest.java
@@ -11,6 +11,8 @@ package tests.api.java.util.concurrent;
import junit.framework.*;
import java.util.*;
import java.util.concurrent.*;
+import java.util.concurrent.locks.*;
+import java.util.concurrent.atomic.*;
public class CyclicBarrierTest extends JSR166TestCase{
public static void main(String[] args) {
@@ -164,7 +166,7 @@ public class CyclicBarrierTest extends JSR166TestCase{
* throw BrokenBarrierException
*/
public void testAwait2_Interrupted_BrokenBarrier() {
- final CyclicBarrier c = new CyclicBarrier(3);
+ final CyclicBarrier c = new CyclicBarrier(3);
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
@@ -229,7 +231,7 @@ public class CyclicBarrierTest extends JSR166TestCase{
* throw BrokenBarrierException
*/
public void testAwait4_Timeout_BrokenBarrier() {
- final CyclicBarrier c = new CyclicBarrier(3);
+ final CyclicBarrier c = new CyclicBarrier(3);
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
@@ -267,7 +269,7 @@ public class CyclicBarrierTest extends JSR166TestCase{
* throw BrokenBarrierException
*/
public void testAwait5_Timeout_BrokenBarrier() {
- final CyclicBarrier c = new CyclicBarrier(3);
+ final CyclicBarrier c = new CyclicBarrier(3);
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
@@ -376,4 +378,249 @@ public class CyclicBarrierTest extends JSR166TestCase{
}
}
+ /**
+ * All threads block while a barrier is broken.
+ */
+ public void testReset_Leakage() {
+ try {
+ final CyclicBarrier c = new CyclicBarrier(2);
+ final AtomicBoolean done = new AtomicBoolean();
+ Thread t = new Thread() {
+ public void run() {
+ while (!done.get()) {
+ try {
+ while (c.isBroken())
+ c.reset();
+
+ c.await();
+ threadFail("await should not return");
+ }
+ catch (BrokenBarrierException e) {
+ }
+ catch (InterruptedException ie) {
+ }
+ }
+ }
+ };
+
+ t.start();
+ for( int i = 0; i < 4; i++) {
+ Thread.sleep(SHORT_DELAY_MS);
+ t.interrupt();
+ }
+ done.set(true);
+ t.interrupt();
+ }
+ catch (Exception ex) {
+ unexpectedException();
+ }
+ }
+
+ /**
+ * Reset of a non-broken barrier does not break barrier
+ */
+ public void testResetWithoutBreakage() {
+ try {
+ final CyclicBarrier start = new CyclicBarrier(3);
+ final CyclicBarrier barrier = new CyclicBarrier(3);
+ for (int i = 0; i < 3; i++) {
+ Thread t1 = new Thread(new Runnable() {
+ public void run() {
+ try { start.await(); }
+ catch (Exception ie) {
+ threadFail("start barrier");
+ }
+ try { barrier.await(); }
+ catch (Throwable thrown) {
+ unexpectedException();
+ }}});
+
+ Thread t2 = new Thread(new Runnable() {
+ public void run() {
+ try { start.await(); }
+ catch (Exception ie) {
+ threadFail("start barrier");
+ }
+ try { barrier.await(); }
+ catch (Throwable thrown) {
+ unexpectedException();
+ }}});
+
+
+ t1.start();
+ t2.start();
+ try { start.await(); }
+ catch (Exception ie) { threadFail("start barrier"); }
+ barrier.await();
+ t1.join();
+ t2.join();
+ assertFalse(barrier.isBroken());
+ assertEquals(0, barrier.getNumberWaiting());
+ if (i == 1) barrier.reset();
+ assertFalse(barrier.isBroken());
+ assertEquals(0, barrier.getNumberWaiting());
+ }
+ }
+ catch (Exception ex) {
+ unexpectedException();
+ }
+ }
+
+ /**
+ * Reset of a barrier after interruption reinitializes it.
+ */
+ public void testResetAfterInterrupt() {
+ try {
+ final CyclicBarrier start = new CyclicBarrier(3);
+ final CyclicBarrier barrier = new CyclicBarrier(3);
+ for (int i = 0; i < 2; i++) {
+ Thread t1 = new Thread(new Runnable() {
+ public void run() {
+ try { start.await(); }
+ catch (Exception ie) {
+ threadFail("start barrier");
+ }
+ try { barrier.await(); }
+ catch(InterruptedException ok) {}
+ catch (Throwable thrown) {
+ unexpectedException();
+ }}});
+
+ Thread t2 = new Thread(new Runnable() {
+ public void run() {
+ try { start.await(); }
+ catch (Exception ie) {
+ threadFail("start barrier");
+ }
+ try { barrier.await(); }
+ catch(BrokenBarrierException ok) {}
+ catch (Throwable thrown) {
+ unexpectedException();
+ }}});
+
+ t1.start();
+ t2.start();
+ try { start.await(); }
+ catch (Exception ie) { threadFail("start barrier"); }
+ t1.interrupt();
+ t1.join();
+ t2.join();
+ assertTrue(barrier.isBroken());
+ assertEquals(0, barrier.getNumberWaiting());
+ barrier.reset();
+ assertFalse(barrier.isBroken());
+ assertEquals(0, barrier.getNumberWaiting());
+ }
+ }
+ catch (Exception ex) {
+ unexpectedException();
+ }
+ }
+
+ /**
+ * Reset of a barrier after timeout reinitializes it.
+ */
+ public void testResetAfterTimeout() {
+ try {
+ final CyclicBarrier start = new CyclicBarrier(3);
+ final CyclicBarrier barrier = new CyclicBarrier(3);
+ for (int i = 0; i < 2; i++) {
+ Thread t1 = new Thread(new Runnable() {
+ public void run() {
+ try { start.await(); }
+ catch (Exception ie) {
+ threadFail("start barrier");
+ }
+ try { barrier.await(MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS); }
+ catch(TimeoutException ok) {}
+ catch (Throwable thrown) {
+ unexpectedException();
+ }}});
+
+ Thread t2 = new Thread(new Runnable() {
+ public void run() {
+ try { start.await(); }
+ catch (Exception ie) {
+ threadFail("start barrier");
+ }
+ try { barrier.await(); }
+ catch(BrokenBarrierException ok) {}
+ catch (Throwable thrown) {
+ unexpectedException();
+ }}});
+
+ t1.start();
+ t2.start();
+ try { start.await(); }
+ catch (Exception ie) { threadFail("start barrier"); }
+ t1.join();
+ t2.join();
+ assertTrue(barrier.isBroken());
+ assertEquals(0, barrier.getNumberWaiting());
+ barrier.reset();
+ assertFalse(barrier.isBroken());
+ assertEquals(0, barrier.getNumberWaiting());
+ }
+ }
+ catch (Exception ex) {
+ unexpectedException();
+ }
+ }
+
+
+ /**
+ * Reset of a barrier after a failed command reinitializes it.
+ */
+ public void testResetAfterCommandException() {
+ try {
+ final CyclicBarrier start = new CyclicBarrier(3);
+ final CyclicBarrier barrier =
+ new CyclicBarrier(3, new Runnable() {
+ public void run() {
+ throw new NullPointerException(); }});
+ for (int i = 0; i < 2; i++) {
+ Thread t1 = new Thread(new Runnable() {
+ public void run() {
+ try { start.await(); }
+ catch (Exception ie) {
+ threadFail("start barrier");
+ }
+ try { barrier.await(); }
+ catch(BrokenBarrierException ok) {}
+ catch (Throwable thrown) {
+ unexpectedException();
+ }}});
+
+ Thread t2 = new Thread(new Runnable() {
+ public void run() {
+ try { start.await(); }
+ catch (Exception ie) {
+ threadFail("start barrier");
+ }
+ try { barrier.await(); }
+ catch(BrokenBarrierException ok) {}
+ catch (Throwable thrown) {
+ unexpectedException();
+ }}});
+
+ t1.start();
+ t2.start();
+ try { start.await(); }
+ catch (Exception ie) { threadFail("start barrier"); }
+ while (barrier.getNumberWaiting() < 2) { Thread.yield(); }
+ try { barrier.await(); }
+ catch (Exception ok) { }
+ t1.join();
+ t2.join();
+ assertTrue(barrier.isBroken());
+ assertEquals(0, barrier.getNumberWaiting());
+ barrier.reset();
+ assertFalse(barrier.isBroken());
+ assertEquals(0, barrier.getNumberWaiting());
+ }
+ }
+ catch (Exception ex) {
+ unexpectedException();
+ }
+ }
}
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/DelayQueueTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/DelayQueueTest.java
index e332513..978edb4 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/DelayQueueTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/DelayQueueTest.java
@@ -14,11 +14,11 @@ import java.util.concurrent.*;
public class DelayQueueTest extends JSR166TestCase {
public static void main(String[] args) {
- junit.textui.TestRunner.run (suite());
+ junit.textui.TestRunner.run (suite());
}
public static Test suite() {
- return new TestSuite(DelayQueueTest.class);
+ return new TestSuite(DelayQueueTest.class);
}
private static final int NOCAP = Integer.MAX_VALUE;
@@ -27,20 +27,19 @@ public class DelayQueueTest extends JSR166TestCase {
* A delayed implementation for testing.
* Most tests use Pseudodelays, where delays are all elapsed
* (so, no blocking solely for delays) but are still ordered
- */
- static class PDelay implements Delayed {
+ */
+ static class PDelay implements Delayed {
int pseudodelay;
PDelay(int i) { pseudodelay = Integer.MIN_VALUE + i; }
- // BEGIN android-changed
- public int compareTo(Delayed y) {
+ public int compareTo(PDelay y) {
int i = pseudodelay;
int j = ((PDelay)y).pseudodelay;
if (i < j) return -1;
if (i > j) return 1;
return 0;
}
- // END android-changed
- public int compareTo(PDelay y) {
+
+ public int compareTo(Delayed y) {
int i = pseudodelay;
int j = ((PDelay)y).pseudodelay;
if (i < j) return -1;
@@ -72,21 +71,20 @@ public class DelayQueueTest extends JSR166TestCase {
/**
* Delayed implementation that actually delays
*/
- static class NanoDelay implements Delayed {
+ static class NanoDelay implements Delayed {
long trigger;
- NanoDelay(long i) {
+ NanoDelay(long i) {
trigger = System.nanoTime() + i;
}
- // BEGIN android-changed
- public int compareTo(Delayed y) {
+ public int compareTo(NanoDelay y) {
long i = trigger;
long j = ((NanoDelay)y).trigger;
if (i < j) return -1;
if (i > j) return 1;
return 0;
}
- // END android-changed
- public int compareTo(NanoDelay y) {
+
+ public int compareTo(Delayed y) {
long i = trigger;
long j = ((NanoDelay)y).trigger;
if (i < j) return -1;
@@ -123,16 +121,16 @@ public class DelayQueueTest extends JSR166TestCase {
private DelayQueue populatedQueue(int n) {
DelayQueue q = new DelayQueue();
assertTrue(q.isEmpty());
- for(int i = n-1; i >= 0; i-=2)
- assertTrue(q.offer(new PDelay(i)));
- for(int i = (n & 1); i < n; i+=2)
- assertTrue(q.offer(new PDelay(i)));
+ for(int i = n-1; i >= 0; i-=2)
+ assertTrue(q.offer(new PDelay(i)));
+ for(int i = (n & 1); i < n; i+=2)
+ assertTrue(q.offer(new PDelay(i)));
assertFalse(q.isEmpty());
assertEquals(NOCAP, q.remainingCapacity());
- assertEquals(n, q.size());
+ assertEquals(n, q.size());
return q;
}
-
+
/**
* A new queue has unbounded capacity
*/
@@ -229,22 +227,22 @@ public class DelayQueueTest extends JSR166TestCase {
* offer(null) throws NPE
*/
public void testOfferNull() {
- try {
+ try {
DelayQueue q = new DelayQueue();
q.offer(null);
shouldThrow();
- } catch (NullPointerException success) { }
+ } catch (NullPointerException success) { }
}
/**
* add(null) throws NPE
*/
public void testAddNull() {
- try {
+ try {
DelayQueue q = new DelayQueue();
q.add(null);
shouldThrow();
- } catch (NullPointerException success) { }
+ } catch (NullPointerException success) { }
}
/**
@@ -342,13 +340,13 @@ public class DelayQueueTest extends JSR166TestCase {
* put(null) throws NPE
*/
public void testPutNull() {
- try {
+ try {
DelayQueue q = new DelayQueue();
q.put(null);
shouldThrow();
- }
+ }
catch (NullPointerException success){
- }
+ }
}
/**
@@ -416,7 +414,7 @@ public class DelayQueueTest extends JSR166TestCase {
} finally { }
}
});
-
+
try {
t.start();
Thread.sleep(SMALL_DELAY_MS);
@@ -431,14 +429,14 @@ public class DelayQueueTest extends JSR166TestCase {
* take retrieves elements in priority order
*/
public void testTake() {
- try {
+ try {
DelayQueue q = populatedQueue(SIZE);
for (int i = 0; i < SIZE; ++i) {
assertEquals(new PDelay(i), ((PDelay)q.take()));
}
} catch (InterruptedException e){
- unexpectedException();
- }
+ unexpectedException();
+ }
}
/**
@@ -450,8 +448,8 @@ public class DelayQueueTest extends JSR166TestCase {
public void run() {
try {
q.take();
- threadShouldThrow();
- } catch (InterruptedException success){ }
+ threadShouldThrow();
+ } catch (InterruptedException success){ }
}
});
try {
@@ -478,16 +476,16 @@ public class DelayQueueTest extends JSR166TestCase {
q.take();
threadShouldThrow();
} catch (InterruptedException success){
- }
+ }
}});
t.start();
- try {
- Thread.sleep(SHORT_DELAY_MS);
+ try {
+ Thread.sleep(SHORT_DELAY_MS);
t.interrupt();
t.join();
}
catch (InterruptedException ie) {
- unexpectedException();
+ unexpectedException();
}
}
@@ -500,7 +498,7 @@ public class DelayQueueTest extends JSR166TestCase {
for (int i = 0; i < SIZE; ++i) {
assertEquals(new PDelay(i), ((PDelay)q.poll()));
}
- assertNull(q.poll());
+ assertNull(q.poll());
}
/**
@@ -514,8 +512,8 @@ public class DelayQueueTest extends JSR166TestCase {
}
assertNull(q.poll(0, TimeUnit.MILLISECONDS));
} catch (InterruptedException e){
- unexpectedException();
- }
+ unexpectedException();
+ }
}
/**
@@ -529,8 +527,8 @@ public class DelayQueueTest extends JSR166TestCase {
}
assertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException e){
- unexpectedException();
- }
+ unexpectedException();
+ }
}
/**
@@ -547,16 +545,16 @@ public class DelayQueueTest extends JSR166TestCase {
}
threadAssertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException success){
- }
+ }
}});
t.start();
- try {
- Thread.sleep(SHORT_DELAY_MS);
+ try {
+ Thread.sleep(SHORT_DELAY_MS);
t.interrupt();
t.join();
}
catch (InterruptedException ie) {
- unexpectedException();
+ unexpectedException();
}
}
@@ -572,8 +570,8 @@ public class DelayQueueTest extends JSR166TestCase {
threadAssertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS));
q.poll(LONG_DELAY_MS, TimeUnit.MILLISECONDS);
q.poll(LONG_DELAY_MS, TimeUnit.MILLISECONDS);
- threadFail("Should block");
- } catch (InterruptedException success) { }
+ threadFail("Should block");
+ } catch (InterruptedException success) { }
}
});
try {
@@ -585,7 +583,7 @@ public class DelayQueueTest extends JSR166TestCase {
} catch (Exception e){
unexpectedException();
}
- }
+ }
/**
@@ -596,10 +594,12 @@ public class DelayQueueTest extends JSR166TestCase {
for (int i = 0; i < SIZE; ++i) {
assertEquals(new PDelay(i), ((PDelay)q.peek()));
q.poll();
- assertTrue(q.peek() == null ||
- i != ((PDelay)q.peek()).intValue());
+ if (q.isEmpty())
+ assertNull(q.peek());
+ else
+ assertTrue(i != ((PDelay)q.peek()).intValue());
}
- assertNull(q.peek());
+ assertNull(q.peek());
}
/**
@@ -630,7 +630,7 @@ public class DelayQueueTest extends JSR166TestCase {
q.remove();
shouldThrow();
} catch (NoSuchElementException success){
- }
+ }
}
/**
@@ -647,7 +647,7 @@ public class DelayQueueTest extends JSR166TestCase {
}
assertTrue(q.isEmpty());
}
-
+
/**
* contains(x) reports true when elements added but not yet removed
*/
@@ -669,8 +669,10 @@ public class DelayQueueTest extends JSR166TestCase {
assertTrue(q.isEmpty());
assertEquals(0, q.size());
assertEquals(NOCAP, q.remainingCapacity());
- q.add(new PDelay(1));
+ PDelay x = new PDelay(1);
+ q.add(x);
assertFalse(q.isEmpty());
+ assertTrue(q.contains(x));
q.clear();
assertTrue(q.isEmpty());
}
@@ -729,14 +731,14 @@ public class DelayQueueTest extends JSR166TestCase {
*/
public void testToArray() {
DelayQueue q = populatedQueue(SIZE);
- Object[] o = q.toArray();
+ Object[] o = q.toArray();
Arrays.sort(o);
- try {
- for(int i = 0; i < o.length; i++)
- assertEquals(o[i], q.take());
- } catch (InterruptedException e){
- unexpectedException();
- }
+ try {
+ for(int i = 0; i < o.length; i++)
+ assertEquals(o[i], q.take());
+ } catch (InterruptedException e){
+ unexpectedException();
+ }
}
/**
@@ -744,15 +746,15 @@ public class DelayQueueTest extends JSR166TestCase {
*/
public void testToArray2() {
DelayQueue q = populatedQueue(SIZE);
- PDelay[] ints = new PDelay[SIZE];
- ints = (PDelay[])q.toArray(ints);
+ PDelay[] ints = new PDelay[SIZE];
+ ints = (PDelay[])q.toArray(ints);
Arrays.sort(ints);
- try {
- for(int i = 0; i < ints.length; i++)
- assertEquals(ints[i], q.take());
- } catch (InterruptedException e){
- unexpectedException();
- }
+ try {
+ for(int i = 0; i < ints.length; i++)
+ assertEquals(ints[i], q.take());
+ } catch (InterruptedException e){
+ unexpectedException();
+ }
}
@@ -760,31 +762,31 @@ public class DelayQueueTest extends JSR166TestCase {
* toArray(null) throws NPE
*/
public void testToArray_BadArg() {
- try {
+ try {
DelayQueue q = populatedQueue(SIZE);
- Object o[] = q.toArray(null);
- shouldThrow();
- } catch(NullPointerException success){}
+ Object o[] = q.toArray(null);
+ shouldThrow();
+ } catch(NullPointerException success){}
}
/**
* toArray with incompatible array type throws CCE
*/
public void testToArray1_BadArg() {
- try {
+ try {
DelayQueue q = populatedQueue(SIZE);
- Object o[] = q.toArray(new String[10] );
- shouldThrow();
- } catch(ArrayStoreException success){}
+ Object o[] = q.toArray(new String[10] );
+ shouldThrow();
+ } catch(ArrayStoreException success){}
}
-
+
/**
* iterator iterates through all elements
*/
public void testIterator() {
DelayQueue q = populatedQueue(SIZE);
int i = 0;
- Iterator it = q.iterator();
+ Iterator it = q.iterator();
while(it.hasNext()) {
assertTrue(q.contains(it.next()));
++i;
@@ -819,7 +821,7 @@ public class DelayQueueTest extends JSR166TestCase {
for (int i = 0; i < SIZE; ++i) {
assertTrue(s.indexOf(String.valueOf(Integer.MIN_VALUE+i)) >= 0);
}
- }
+ }
/**
* offer transfers elements across Executor tasks
@@ -875,7 +877,7 @@ public class DelayQueueTest extends JSR166TestCase {
NanoDelay e = (NanoDelay)(q.take());
long tt = e.getTriggerTime();
assertTrue(tt <= System.nanoTime());
- if (i != 0)
+ if (i != 0)
assertTrue(tt >= last);
last = tt;
}
@@ -885,10 +887,41 @@ public class DelayQueueTest extends JSR166TestCase {
}
}
+ /**
+ * peek of a non-empty queue returns non-null even if not expired
+ */
+ public void testPeekDelayed() {
+ DelayQueue q = new DelayQueue();
+ q.add(new NanoDelay(Long.MAX_VALUE));
+ assert(q.peek() != null);
+ }
+
+
+ /**
+ * poll of a non-empty queue returns null if no expired elements.
+ */
+ public void testPollDelayed() {
+ DelayQueue q = new DelayQueue();
+ q.add(new NanoDelay(Long.MAX_VALUE));
+ assertNull(q.poll());
+ }
+
+ /**
+ * timed poll of a non-empty queue returns null if no expired elements.
+ */
+ public void testTimedPollDelayed() {
+ DelayQueue q = new DelayQueue();
+ q.add(new NanoDelay(LONG_DELAY_MS * 1000000L));
+ try {
+ assertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS));
+ } catch (Exception ex) {
+ unexpectedException();
+ }
+ }
/**
* drainTo(null) throws NPE
- */
+ */
public void testDrainToNull() {
DelayQueue q = populatedQueue(SIZE);
try {
@@ -900,7 +933,7 @@ public class DelayQueueTest extends JSR166TestCase {
/**
* drainTo(this) throws IAE
- */
+ */
public void testDrainToSelf() {
DelayQueue q = populatedQueue(SIZE);
try {
@@ -912,13 +945,30 @@ public class DelayQueueTest extends JSR166TestCase {
/**
* drainTo(c) empties queue into another collection c
- */
+ */
public void testDrainTo() {
- DelayQueue q = populatedQueue(SIZE);
+ DelayQueue q = new DelayQueue();
+ PDelay[] elems = new PDelay[SIZE];
+ for (int i = 0; i < SIZE; ++i) {
+ elems[i] = new PDelay(i);
+ q.add(elems[i]);
+ }
ArrayList l = new ArrayList();
q.drainTo(l);
assertEquals(q.size(), 0);
- assertEquals(l.size(), SIZE);
+ for (int i = 0; i < SIZE; ++i)
+ assertEquals(l.get(i), elems[i]);
+ q.add(elems[0]);
+ q.add(elems[1]);
+ assertFalse(q.isEmpty());
+ assertTrue(q.contains(elems[0]));
+ assertTrue(q.contains(elems[1]));
+ l.clear();
+ q.drainTo(l);
+ assertEquals(q.size(), 0);
+ assertEquals(l.size(), 2);
+ for (int i = 0; i < 2; ++i)
+ assertEquals(l.get(i), elems[i]);
}
/**
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorCompletionServiceTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorCompletionServiceTest.java
index 2e237e2..b1988cc 100644
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorCompletionServiceTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorCompletionServiceTest.java
@@ -11,6 +11,7 @@ package tests.api.java.util.concurrent;
import junit.framework.*;
import java.util.*;
import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
import java.math.BigInteger;
import java.security.*;
@@ -88,7 +89,7 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase{
Callable c = new StringTask();
ecs.submit(c);
Future f = ecs.take();
- assert(f.isDone());
+ assertTrue(f.isDone());
} catch (Exception ex) {
unexpectedException();
} finally {
@@ -128,7 +129,7 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase{
for (;;) {
Future f = ecs.poll();
if (f != null) {
- assert(f.isDone());
+ assertTrue(f.isDone());
break;
}
}
@@ -151,12 +152,11 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase{
ecs.submit(c);
Future f = ecs.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS);
if (f != null)
- assert(f.isDone());
+ assertTrue(f.isDone());
} catch (Exception ex) {
unexpectedException();
} finally {
joinPool(e);
}
}
-
}
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorsTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorsTest.java
index 40e23e4..e8fc7e5 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorsTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorsTest.java
@@ -16,7 +16,7 @@ import java.security.*;
public class ExecutorsTest extends JSR166TestCase{
public static void main(String[] args) {
- junit.textui.TestRunner.run (suite());
+ junit.textui.TestRunner.run (suite());
}
public static Test suite() {
return new TestSuite(ExecutorsTest.class);
@@ -26,13 +26,13 @@ public class ExecutorsTest extends JSR166TestCase{
private final ExecutorService exec;
private final Callable<T> func;
private final long msecs;
-
+
TimedCallable(ExecutorService exec, Callable<T> func, long msecs) {
this.exec = exec;
this.func = func;
this.msecs = msecs;
}
-
+
public T call() throws Exception {
Future<T> ftask = exec.submit(func);
try {
@@ -295,13 +295,13 @@ public class ExecutorsTest extends JSR166TestCase{
List<Callable<BigInteger>> tasks = new ArrayList<Callable<BigInteger>>(N);
try {
long startTime = System.currentTimeMillis();
-
+
long i = 0;
while (tasks.size() < N) {
tasks.add(new TimedCallable<BigInteger>(executor, new Fib(i), 1));
i += 10;
}
-
+
int iters = 0;
BigInteger sum = BigInteger.ZERO;
for (Iterator<Callable<BigInteger>> it = tasks.iterator(); it.hasNext();) {
@@ -326,7 +326,7 @@ public class ExecutorsTest extends JSR166TestCase{
}
}
-
+
/**
* ThreadPoolExecutor using defaultThreadFactory has
* specified group, priority, daemon status, and name
@@ -335,31 +335,31 @@ public class ExecutorsTest extends JSR166TestCase{
final ThreadGroup egroup = Thread.currentThread().getThreadGroup();
Runnable r = new Runnable() {
public void run() {
- try {
- Thread current = Thread.currentThread();
- threadAssertTrue(!current.isDaemon());
- threadAssertTrue(current.getPriority() == Thread.NORM_PRIORITY);
- ThreadGroup g = current.getThreadGroup();
- SecurityManager s = System.getSecurityManager();
- if (s != null)
- threadAssertTrue(g == s.getThreadGroup());
- else
- threadAssertTrue(g == egroup);
- String name = current.getName();
- threadAssertTrue(name.endsWith("thread-1"));
- } catch (SecurityException ok) {
- // Also pass if not allowed to change setting
- }
+ try {
+ Thread current = Thread.currentThread();
+ threadAssertTrue(!current.isDaemon());
+ threadAssertTrue(current.getPriority() <= Thread.NORM_PRIORITY);
+ ThreadGroup g = current.getThreadGroup();
+ SecurityManager s = System.getSecurityManager();
+ if (s != null)
+ threadAssertTrue(g == s.getThreadGroup());
+ else
+ threadAssertTrue(g == egroup);
+ String name = current.getName();
+ threadAssertTrue(name.endsWith("thread-1"));
+ } catch (SecurityException ok) {
+ // Also pass if not allowed to change setting
+ }
}
};
ExecutorService e = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory());
-
+
e.execute(r);
try {
e.shutdown();
} catch(SecurityException ok) {
}
-
+
try {
Thread.sleep(SHORT_DELAY_MS);
} catch (Exception eX) {
@@ -390,27 +390,27 @@ public class ExecutorsTest extends JSR166TestCase{
final AccessControlContext thisacc = AccessController.getContext();
Runnable r = new Runnable() {
public void run() {
- try {
- Thread current = Thread.currentThread();
- threadAssertTrue(!current.isDaemon());
- threadAssertTrue(current.getPriority() == Thread.NORM_PRIORITY);
- ThreadGroup g = current.getThreadGroup();
- SecurityManager s = System.getSecurityManager();
- if (s != null)
- threadAssertTrue(g == s.getThreadGroup());
- else
- threadAssertTrue(g == egroup);
- String name = current.getName();
- threadAssertTrue(name.endsWith("thread-1"));
- threadAssertTrue(thisccl == current.getContextClassLoader());
- threadAssertTrue(thisacc.equals(AccessController.getContext()));
- } catch(SecurityException ok) {
- // Also pass if not allowed to change settings
- }
+ try {
+ Thread current = Thread.currentThread();
+ threadAssertTrue(!current.isDaemon());
+ threadAssertTrue(current.getPriority() <= Thread.NORM_PRIORITY);
+ ThreadGroup g = current.getThreadGroup();
+ SecurityManager s = System.getSecurityManager();
+ if (s != null)
+ threadAssertTrue(g == s.getThreadGroup());
+ else
+ threadAssertTrue(g == egroup);
+ String name = current.getName();
+ threadAssertTrue(name.endsWith("thread-1"));
+ threadAssertTrue(thisccl == current.getContextClassLoader());
+ threadAssertTrue(thisacc.equals(AccessController.getContext()));
+ } catch(SecurityException ok) {
+ // Also pass if not allowed to change settings
+ }
}
};
ExecutorService e = Executors.newSingleThreadExecutor(Executors.privilegedThreadFactory());
-
+
Policy.setPolicy(savedPolicy);
e.execute(r);
try {
@@ -460,7 +460,7 @@ public class ExecutorsTest extends JSR166TestCase{
Policy.setPolicy(savedPolicy);
return;
} catch(AccessControlException ok) {
- }
+ }
try {
Callable task = Executors.privilegedCallableUsingCurrentClassLoader(new NoOpCallable());
@@ -468,7 +468,7 @@ public class ExecutorsTest extends JSR166TestCase{
} catch(AccessControlException success) {
} catch(Exception ex) {
unexpectedException();
- }
+ }
finally {
Policy.setPolicy(savedPolicy);
}
@@ -489,13 +489,13 @@ public class ExecutorsTest extends JSR166TestCase{
} catch (AccessControlException ok) {
return;
}
-
+
try {
Callable task = Executors.privilegedCallableUsingCurrentClassLoader(new NoOpCallable());
task.call();
} catch(Exception ex) {
unexpectedException();
- }
+ }
finally {
Policy.setPolicy(savedPolicy);
}
@@ -520,7 +520,7 @@ public class ExecutorsTest extends JSR166TestCase{
return; // program has too few permissions to set up test
}
- // Make sure that program doesn't have too many permissions
+ // Make sure that program doesn't have too many permissions
try {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
@@ -538,7 +538,7 @@ public class ExecutorsTest extends JSR166TestCase{
} catch(AccessControlException success) {
} catch(Exception ex) {
unexpectedException();
- }
+ }
}
/**
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/JSR166TestCase.java b/concurrent/src/test/java/tests/api/java/util/concurrent/JSR166TestCase.java
index 70dafbc..c900616 100644
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/JSR166TestCase.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/JSR166TestCase.java
@@ -2,8 +2,8 @@
* 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
- * Other contributors include Andrew Wright, Jeffrey Hayes,
- * Pat Fisher, Mike Judd.
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
*/
package tests.api.java.util.concurrent;
@@ -19,13 +19,13 @@ import java.security.*;
* utility methods and classes, as well as a simple framework for
* helping to make sure that assertions failing in generated threads
* cause the associated test that generated them to itself fail (which
- * JUnit doe not otherwise arrange). The rules for creating such
+ * JUnit does not otherwise arrange). The rules for creating such
* tests are:
*
* <ol>
*
* <li> All assertions in code running in generated threads must use
- * the forms {@link #threadFail} , {@link #threadAssertTrue} {@link
+ * the forms {@link #threadFail}, {@link #threadAssertTrue}, {@link
* #threadAssertEquals}, or {@link #threadAssertNull}, (not
* <tt>fail</tt>, <tt>assertTrue</tt>, etc.) It is OK (but not
* particularly recommended) for other code to use these forms too.
@@ -46,7 +46,7 @@ import java.security.*;
* is always discriminable as larger than SHORT and smaller than
* MEDIUM. And so on. These constants are set to conservative values,
* but even so, if there is ever any doubt, they can all be increased
- * in one spot to rerun tests on slower platforms</li>
+ * in one spot to rerun tests on slower platforms.</li>
*
* <li> All threads generated must be joined inside each test case
* method (or <tt>fail</tt> to do so) before returning from the
@@ -65,7 +65,7 @@ import java.security.*;
* "normal" behaviors differ significantly. And sometimes testcases
* cover multiple methods when they cannot be tested in
* isolation.</li>
- *
+ *
* <li> The documentation style for testcases is to provide as javadoc
* a simple sentence or two describing the property that the testcase
* method purports to test. The javadocs do not say anything about how
@@ -90,10 +90,10 @@ import java.security.*;
public class JSR166TestCase extends TestCase {
/**
* Runs all JSR166 unit tests using junit.textui.TestRunner
- */
+ */
public static void main (String[] args) {
int iters = 1;
- if (args.length > 0)
+ if (args.length > 0)
iters = Integer.parseInt(args[0]);
Test s = suite();
for (int i = 0; i < iters; ++i) {
@@ -106,7 +106,7 @@ public class JSR166TestCase extends TestCase {
/**
* Collects all JSR166 unit tests as one suite
- */
+ */
public static Test suite ( ) {
TestSuite suite = tests.TestSuiteFactory.createTestSuite("JSR166 Unit Tests");
// BEGIN android-changed
@@ -114,18 +114,18 @@ public class JSR166TestCase extends TestCase {
suite.addTest(AbstractQueueTest.suite());
suite.addTest(AbstractQueuedSynchronizerTest.suite());
suite.addTest(ArrayBlockingQueueTest.suite());
- suite.addTest(AtomicBooleanTest.suite());
- suite.addTest(AtomicIntegerArrayTest.suite());
- suite.addTest(AtomicIntegerFieldUpdaterTest.suite());
- suite.addTest(AtomicIntegerTest.suite());
- suite.addTest(AtomicLongArrayTest.suite());
- suite.addTest(AtomicLongFieldUpdaterTest.suite());
- suite.addTest(AtomicLongTest.suite());
- suite.addTest(AtomicMarkableReferenceTest.suite());
- suite.addTest(AtomicReferenceArrayTest.suite());
- suite.addTest(AtomicReferenceFieldUpdaterTest.suite());
- suite.addTest(AtomicReferenceTest.suite());
- suite.addTest(AtomicStampedReferenceTest.suite());
+ suite.addTest(AtomicBooleanTest.suite());
+ suite.addTest(AtomicIntegerArrayTest.suite());
+ suite.addTest(AtomicIntegerFieldUpdaterTest.suite());
+ suite.addTest(AtomicIntegerTest.suite());
+ suite.addTest(AtomicLongArrayTest.suite());
+ suite.addTest(AtomicLongFieldUpdaterTest.suite());
+ suite.addTest(AtomicLongTest.suite());
+ suite.addTest(AtomicMarkableReferenceTest.suite());
+ suite.addTest(AtomicReferenceArrayTest.suite());
+ suite.addTest(AtomicReferenceFieldUpdaterTest.suite());
+ suite.addTest(AtomicReferenceTest.suite());
+ suite.addTest(AtomicStampedReferenceTest.suite());
suite.addTest(ConcurrentHashMapTest.suite());
suite.addTest(ConcurrentLinkedQueueTest.suite());
suite.addTest(CopyOnWriteArrayListTest.suite());
@@ -164,16 +164,16 @@ public class JSR166TestCase extends TestCase {
/**
- * Return the shortest timed delay. This could
+ * Returns the shortest timed delay. This could
* be reimplemented to use for example a Property.
- */
+ */
protected long getShortDelay() {
return 50;
}
/**
- * Set delays as multiples of SHORT_DELAY.
+ * Sets delays as multiples of SHORT_DELAY.
*/
protected void setDelays() {
SHORT_DELAY_MS = getShortDelay();
@@ -188,23 +188,23 @@ public class JSR166TestCase extends TestCase {
volatile boolean threadFailed;
/**
- * Initialize test to indicate that no thread assertions have failed
+ * Initializes test to indicate that no thread assertions have failed
*/
- public void setUp() {
+ public void setUp() {
setDelays();
- threadFailed = false;
+ threadFailed = false;
}
/**
- * Trigger test case failure if any thread assertions have failed
+ * Triggers test case failure if any thread assertions have failed
*/
- public void tearDown() {
- assertFalse(threadFailed);
+ public void tearDown() {
+ assertFalse(threadFailed);
}
/**
* Fail, also setting status to indicate current testcase should fail
- */
+ */
public void threadFail(String reason) {
threadFailed = true;
fail(reason);
@@ -213,7 +213,7 @@ public class JSR166TestCase extends TestCase {
/**
* If expression not true, set status to indicate current testcase
* should fail
- */
+ */
public void threadAssertTrue(boolean b) {
if (!b) {
threadFailed = true;
@@ -224,7 +224,7 @@ public class JSR166TestCase extends TestCase {
/**
* If expression not false, set status to indicate current testcase
* should fail
- */
+ */
public void threadAssertFalse(boolean b) {
if (b) {
threadFailed = true;
@@ -235,7 +235,7 @@ public class JSR166TestCase extends TestCase {
/**
* If argument not null, set status to indicate current testcase
* should fail
- */
+ */
public void threadAssertNull(Object x) {
if (x != null) {
threadFailed = true;
@@ -246,7 +246,7 @@ public class JSR166TestCase extends TestCase {
/**
* If arguments not equal, set status to indicate current testcase
* should fail
- */
+ */
public void threadAssertEquals(long x, long y) {
if (x != y) {
threadFailed = true;
@@ -257,7 +257,7 @@ public class JSR166TestCase extends TestCase {
/**
* If arguments not equal, set status to indicate current testcase
* should fail
- */
+ */
public void threadAssertEquals(Object x, Object y) {
if (x != y && (x == null || !x.equals(y))) {
threadFailed = true;
@@ -267,10 +267,15 @@ public class JSR166TestCase extends TestCase {
/**
* threadFail with message "should throw exception"
- */
+ */
public void threadShouldThrow() {
- threadFailed = true;
- fail("should throw exception");
+ try {
+ threadFailed = true;
+ fail("should throw exception");
+ } catch (AssertionFailedError e) {
+ e.printStackTrace();
+ throw e;
+ }
}
/**
@@ -281,6 +286,14 @@ public class JSR166TestCase extends TestCase {
fail("Unexpected exception");
}
+ /**
+ * threadFail with message "Unexpected exception", with argument
+ */
+ public void threadUnexpectedException(Throwable ex) {
+ threadFailed = true;
+ ex.printStackTrace();
+ fail("Unexpected exception: " + ex);
+ }
/**
* Wait out termination of a thread pool or fail doing so
@@ -299,7 +312,7 @@ public class JSR166TestCase extends TestCase {
/**
* fail with message "should throw exception"
- */
+ */
public void shouldThrow() {
fail("Should throw exception");
}
@@ -334,6 +347,7 @@ public class JSR166TestCase extends TestCase {
static final Integer m3 = new Integer(-3);
static final Integer m4 = new Integer(-4);
static final Integer m5 = new Integer(-5);
+ static final Integer m6 = new Integer(-6);
static final Integer m10 = new Integer(-10);
@@ -389,7 +403,7 @@ public class JSR166TestCase extends TestCase {
Thread.sleep(SHORT_DELAY_MS);
}
catch(Exception e) {
- threadUnexpectedException();
+ threadUnexpectedException(e);
}
}
}
@@ -411,7 +425,7 @@ public class JSR166TestCase extends TestCase {
Thread.sleep(SMALL_DELAY_MS);
}
catch(Exception e) {
- threadUnexpectedException();
+ threadUnexpectedException(e);
}
}
}
@@ -432,7 +446,7 @@ public class JSR166TestCase extends TestCase {
Thread.sleep(SMALL_DELAY_MS);
}
catch(Exception e) {
- threadUnexpectedException();
+ threadUnexpectedException(e);
}
return Boolean.TRUE;
}
@@ -456,7 +470,7 @@ public class JSR166TestCase extends TestCase {
Thread.sleep(MEDIUM_DELAY_MS);
}
catch(Exception e) {
- threadUnexpectedException();
+ threadUnexpectedException(e);
}
}
}
@@ -498,7 +512,7 @@ public class JSR166TestCase extends TestCase {
static class SimpleThreadFactory implements ThreadFactory{
public Thread newThread(Runnable r){
return new Thread(r);
- }
+ }
}
static class TrackedShortRunnable implements Runnable {
@@ -558,8 +572,8 @@ public class JSR166TestCase extends TestCase {
* For use as RejectedExecutionHandler in constructors
*/
static class NoOpREHandler implements RejectedExecutionHandler{
- public void rejectedExecution(Runnable r, ThreadPoolExecutor executor){}
+ public void rejectedExecution(Runnable r, ThreadPoolExecutor executor){}
}
-
-
+
+
}
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/LinkedBlockingQueueTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/LinkedBlockingQueueTest.java
index 7496a4a..6648afb 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/LinkedBlockingQueueTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/LinkedBlockingQueueTest.java
@@ -16,7 +16,7 @@ import java.io.*;
public class LinkedBlockingQueueTest extends JSR166TestCase {
public static void main(String[] args) {
- junit.textui.TestRunner.run (suite());
+ junit.textui.TestRunner.run (suite());
}
public static Test suite() {
@@ -38,7 +38,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
assertEquals(n, q.size());
return q;
}
-
+
/**
* A new queue has the indicated capacity, or Integer.MAX_VALUE if
* none given
@@ -151,7 +151,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
LinkedBlockingQueue q = new LinkedBlockingQueue(1);
q.offer(null);
shouldThrow();
- } catch (NullPointerException success) { }
+ } catch (NullPointerException success) { }
}
/**
@@ -162,7 +162,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
LinkedBlockingQueue q = new LinkedBlockingQueue(1);
q.add(null);
shouldThrow();
- } catch (NullPointerException success) { }
+ } catch (NullPointerException success) { }
}
/**
@@ -186,7 +186,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
assertEquals(0, q.remainingCapacity());
q.add(new Integer(SIZE));
} catch (IllegalStateException success){
- }
+ }
}
/**
@@ -280,9 +280,9 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
q.put(null);
shouldThrow();
- }
+ }
catch (NullPointerException success){
- }
+ }
catch (InterruptedException ie) {
unexpectedException();
}
@@ -323,11 +323,11 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
threadShouldThrow();
} catch (InterruptedException ie){
threadAssertEquals(added, SIZE);
- }
+ }
}});
t.start();
- try {
- Thread.sleep(SHORT_DELAY_MS);
+ try {
+ Thread.sleep(SHORT_DELAY_MS);
t.interrupt();
t.join();
}
@@ -386,7 +386,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
} catch (InterruptedException success){}
}
});
-
+
try {
t.start();
Thread.sleep(SMALL_DELAY_MS);
@@ -408,7 +408,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
}
} catch (InterruptedException e){
unexpectedException();
- }
+ }
}
/**
@@ -421,7 +421,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
try {
q.take();
threadShouldThrow();
- } catch (InterruptedException success){ }
+ } catch (InterruptedException success){ }
}
});
try {
@@ -448,11 +448,11 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
q.take();
threadShouldThrow();
} catch (InterruptedException success){
- }
+ }
}});
t.start();
- try {
- Thread.sleep(SHORT_DELAY_MS);
+ try {
+ Thread.sleep(SHORT_DELAY_MS);
t.interrupt();
t.join();
}
@@ -485,7 +485,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
assertNull(q.poll(0, TimeUnit.MILLISECONDS));
} catch (InterruptedException e){
unexpectedException();
- }
+ }
}
/**
@@ -500,7 +500,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
assertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException e){
unexpectedException();
- }
+ }
}
/**
@@ -517,11 +517,11 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
}
threadAssertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException success){
- }
+ }
}});
t.start();
- try {
- Thread.sleep(SHORT_DELAY_MS);
+ try {
+ Thread.sleep(SHORT_DELAY_MS);
t.interrupt();
t.join();
}
@@ -543,7 +543,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
q.poll(LONG_DELAY_MS, TimeUnit.MILLISECONDS);
q.poll(LONG_DELAY_MS, TimeUnit.MILLISECONDS);
threadShouldThrow();
- } catch (InterruptedException success) { }
+ } catch (InterruptedException success) { }
}
});
try {
@@ -555,7 +555,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
} catch (Exception e){
unexpectedException();
}
- }
+ }
/**
* peek returns next element, or null if empty
@@ -599,7 +599,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
q.remove();
shouldThrow();
} catch (NoSuchElementException success){
- }
+ }
}
/**
@@ -616,7 +616,24 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
}
assertTrue(q.isEmpty());
}
-
+
+ /**
+ * An add following remove(x) succeeds
+ */
+ public void testRemoveElementAndAdd() {
+ try {
+ LinkedBlockingQueue q = new LinkedBlockingQueue();
+ assertTrue(q.add(new Integer(1)));
+ assertTrue(q.add(new Integer(2)));
+ assertTrue(q.remove(new Integer(1)));
+ assertTrue(q.remove(new Integer(2)));
+ assertTrue(q.add(new Integer(3)));
+ assertTrue(q.take() != null);
+ } catch (Exception e){
+ unexpectedException();
+ }
+ }
+
/**
* contains(x) reports true when elements added but not yet removed
*/
@@ -640,6 +657,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
assertEquals(SIZE, q.remainingCapacity());
q.add(one);
assertFalse(q.isEmpty());
+ assertTrue(q.contains(one));
q.clear();
assertTrue(q.isEmpty());
}
@@ -704,7 +722,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
assertEquals(o[i], q.take());
} catch (InterruptedException e){
unexpectedException();
- }
+ }
}
/**
@@ -719,7 +737,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
assertEquals(ints[i], q.take());
} catch (InterruptedException e){
unexpectedException();
- }
+ }
}
/**
@@ -744,7 +762,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
} catch(ArrayStoreException success){}
}
-
+
/**
* iterator iterates through all elements
*/
@@ -757,7 +775,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
}
} catch (InterruptedException e){
unexpectedException();
- }
+ }
}
/**
@@ -772,7 +790,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
Iterator it = q.iterator();
it.next();
it.remove();
-
+
it = q.iterator();
assertEquals(it.next(), one);
assertEquals(it.next(), three);
@@ -827,7 +845,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
for (int i = 0; i < SIZE; ++i) {
assertTrue(s.indexOf(String.valueOf(i)) >= 0);
}
- }
+ }
/**
@@ -862,7 +880,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
}
}
});
-
+
joinPool(executor);
}
@@ -896,7 +914,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
}
}
});
-
+
joinPool(executor);
}
@@ -916,7 +934,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(bin));
LinkedBlockingQueue r = (LinkedBlockingQueue)in.readObject();
assertEquals(q.size(), r.size());
- while (!q.isEmpty())
+ while (!q.isEmpty())
assertEquals(q.remove(), r.remove());
} catch(Exception e){
unexpectedException();
@@ -925,7 +943,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
/**
* drainTo(null) throws NPE
- */
+ */
public void testDrainToNull() {
LinkedBlockingQueue q = populatedQueue(SIZE);
try {
@@ -937,7 +955,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
/**
* drainTo(this) throws IAE
- */
+ */
public void testDrainToSelf() {
LinkedBlockingQueue q = populatedQueue(SIZE);
try {
@@ -949,27 +967,38 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
/**
* drainTo(c) empties queue into another collection c
- */
+ */
public void testDrainTo() {
LinkedBlockingQueue q = populatedQueue(SIZE);
ArrayList l = new ArrayList();
q.drainTo(l);
assertEquals(q.size(), 0);
assertEquals(l.size(), SIZE);
- for (int i = 0; i < SIZE; ++i)
+ for (int i = 0; i < SIZE; ++i)
+ assertEquals(l.get(i), new Integer(i));
+ q.add(zero);
+ q.add(one);
+ assertFalse(q.isEmpty());
+ assertTrue(q.contains(zero));
+ assertTrue(q.contains(one));
+ l.clear();
+ q.drainTo(l);
+ assertEquals(q.size(), 0);
+ assertEquals(l.size(), 2);
+ for (int i = 0; i < 2; ++i)
assertEquals(l.get(i), new Integer(i));
}
/**
* drainTo empties full queue, unblocking a waiting put.
- */
+ */
public void testDrainToWithActivePut() {
final LinkedBlockingQueue q = populatedQueue(SIZE);
Thread t = new Thread(new Runnable() {
public void run() {
try {
q.put(new Integer(SIZE+1));
- } catch (InterruptedException ie){
+ } catch (InterruptedException ie){
threadUnexpectedException();
}
}
@@ -979,7 +1008,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
ArrayList l = new ArrayList();
q.drainTo(l);
assertTrue(l.size() >= SIZE);
- for (int i = 0; i < SIZE; ++i)
+ for (int i = 0; i < SIZE; ++i)
assertEquals(l.get(i), new Integer(i));
t.join();
assertTrue(q.size() + l.size() >= SIZE);
@@ -990,7 +1019,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
/**
* drainTo(null, n) throws NPE
- */
+ */
public void testDrainToNullN() {
LinkedBlockingQueue q = populatedQueue(SIZE);
try {
@@ -1002,7 +1031,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
/**
* drainTo(this, n) throws IAE
- */
+ */
public void testDrainToSelfN() {
LinkedBlockingQueue q = populatedQueue(SIZE);
try {
@@ -1014,17 +1043,20 @@ public class LinkedBlockingQueueTest extends JSR166TestCase {
/**
* drainTo(c, n) empties first max {n, size} elements of queue into c
- */
+ */
public void testDrainToN() {
+ LinkedBlockingQueue q = new LinkedBlockingQueue();
for (int i = 0; i < SIZE + 2; ++i) {
- LinkedBlockingQueue q = populatedQueue(SIZE);
+ for(int j = 0; j < SIZE; j++)
+ assertTrue(q.offer(new Integer(j)));
ArrayList l = new ArrayList();
q.drainTo(l, i);
int k = (i < SIZE)? i : SIZE;
- assertEquals(q.size(), SIZE-k);
assertEquals(l.size(), k);
- for (int j = 0; j < k; ++j)
+ assertEquals(q.size(), SIZE-k);
+ for (int j = 0; j < k; ++j)
assertEquals(l.get(j), new Integer(j));
+ while (q.poll() != null) ;
}
}
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/LockSupportTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/LockSupportTest.java
index 8d74a3a..b39db2e 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/LockSupportTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/LockSupportTest.java
@@ -15,7 +15,7 @@ import java.util.concurrent.locks.*;
public class LockSupportTest extends JSR166TestCase{
public static void main(String[] args) {
- junit.textui.TestRunner.run (suite());
+ junit.textui.TestRunner.run (suite());
}
public static Test suite() {
return new TestSuite(LockSupportTest.class);
@@ -24,7 +24,7 @@ public class LockSupportTest extends JSR166TestCase{
/**
* park is released by unpark occurring after park
*/
- public void testPark() {
+ public void testPark() {
Thread t = new Thread(new Runnable() {
public void run() {
try {
@@ -48,7 +48,7 @@ public class LockSupportTest extends JSR166TestCase{
/**
* park is released by unpark occurring before park
*/
- public void testPark2() {
+ public void testPark2() {
Thread t = new Thread(new Runnable() {
public void run() {
try {
@@ -70,15 +70,14 @@ public class LockSupportTest extends JSR166TestCase{
}
/**
- * park is released by interrupt
+ * park is released by interrupt
*/
- public void testPark3() {
- Thread t = new Thread(new Runnable() {
- public void run() {
- try {
- LockSupport.park();
- threadAssertTrue(Thread.interrupted());
- } catch(Exception e){
+ public void testPark3() {
+ Thread t = new Thread(new Runnable() {
+ public void run() {
+ try {
+ LockSupport.park();
+ } catch(Exception e){
threadUnexpectedException();
}
}
@@ -97,7 +96,7 @@ public class LockSupportTest extends JSR166TestCase{
/**
* park returns if interrupted before park
*/
- public void testPark4() {
+ public void testPark4() {
final ReentrantLock lock = new ReentrantLock();
lock.lock();
Thread t = new Thread(new Runnable() {
@@ -124,7 +123,7 @@ public class LockSupportTest extends JSR166TestCase{
/**
* parkNanos times out if not unparked
*/
- public void testParkNanos() {
+ public void testParkNanos() {
Thread t = new Thread(new Runnable() {
public void run() {
try {
@@ -147,7 +146,7 @@ public class LockSupportTest extends JSR166TestCase{
/**
* parkUntil times out if not unparked
*/
- public void testParkUntil() {
+ public void testParkUntil() {
Thread t = new Thread(new Runnable() {
public void run() {
try {
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/PriorityBlockingQueueTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/PriorityBlockingQueueTest.java
index b258963..3857e0f 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/PriorityBlockingQueueTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/PriorityBlockingQueueTest.java
@@ -628,9 +628,9 @@ public class PriorityBlockingQueueTest extends JSR166TestCase {
q.clear();
assertTrue(q.isEmpty());
assertEquals(0, q.size());
- assertEquals(NOCAP, q.remainingCapacity());
- q.add(new Integer(1));
+ q.add(one);
assertFalse(q.isEmpty());
+ assertTrue(q.contains(one));
q.clear();
assertTrue(q.isEmpty());
}
@@ -873,6 +873,17 @@ public class PriorityBlockingQueueTest extends JSR166TestCase {
assertEquals(l.size(), SIZE);
for (int i = 0; i < SIZE; ++i)
assertEquals(l.get(i), new Integer(i));
+ q.add(zero);
+ q.add(one);
+ assertFalse(q.isEmpty());
+ assertTrue(q.contains(zero));
+ assertTrue(q.contains(one));
+ l.clear();
+ q.drainTo(l);
+ assertEquals(q.size(), 0);
+ assertEquals(l.size(), 2);
+ for (int i = 0; i < 2; ++i)
+ assertEquals(l.get(i), new Integer(i));
}
/**
@@ -927,15 +938,18 @@ public class PriorityBlockingQueueTest extends JSR166TestCase {
* drainTo(c, n) empties first max {n, size} elements of queue into c
*/
public void testDrainToN() {
+ PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE*2);
for (int i = 0; i < SIZE + 2; ++i) {
- PriorityBlockingQueue q = populatedQueue(SIZE);
+ for(int j = 0; j < SIZE; j++)
+ assertTrue(q.offer(new Integer(j)));
ArrayList l = new ArrayList();
q.drainTo(l, i);
int k = (i < SIZE)? i : SIZE;
- assertEquals(q.size(), SIZE-k);
assertEquals(l.size(), k);
+ assertEquals(q.size(), SIZE-k);
for (int j = 0; j < k; ++j)
- assertTrue(l.contains(new Integer(j)));
+ assertEquals(l.get(j), new Integer(j));
+ while (q.poll() != null) ;
}
}
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantLockTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantLockTest.java
index fdc9f31..c50482d 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantLockTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantLockTest.java
@@ -16,7 +16,7 @@ import java.io.*;
public class ReentrantLockTest extends JSR166TestCase {
public static void main(String[] args) {
- junit.textui.TestRunner.run (suite());
+ junit.textui.TestRunner.run (suite());
}
public static Test suite() {
return new TestSuite(ReentrantLockTest.class);
@@ -56,11 +56,11 @@ public class ReentrantLockTest extends JSR166TestCase {
*/
static class PublicReentrantLock extends ReentrantLock {
PublicReentrantLock() { super(); }
- public Collection<Thread> getQueuedThreads() {
- return super.getQueuedThreads();
+ public Collection<Thread> getQueuedThreads() {
+ return super.getQueuedThreads();
}
- public Collection<Thread> getWaitingThreads(Condition c) {
- return super.getWaitingThreads(c);
+ public Collection<Thread> getWaitingThreads(Condition c) {
+ return super.getWaitingThreads(c);
}
@@ -69,7 +69,7 @@ public class ReentrantLockTest extends JSR166TestCase {
/**
* Constructor sets given fairness
*/
- public void testConstructor() {
+ public void testConstructor() {
ReentrantLock rl = new ReentrantLock();
assertFalse(rl.isFair());
ReentrantLock r2 = new ReentrantLock(true);
@@ -79,7 +79,7 @@ public class ReentrantLockTest extends JSR166TestCase {
/**
* locking an unlocked lock succeeds
*/
- public void testLock() {
+ public void testLock() {
ReentrantLock rl = new ReentrantLock();
rl.lock();
assertTrue(rl.isLocked());
@@ -89,7 +89,7 @@ public class ReentrantLockTest extends JSR166TestCase {
/**
* locking an unlocked fair lock succeeds
*/
- public void testFairLock() {
+ public void testFairLock() {
ReentrantLock rl = new ReentrantLock(true);
rl.lock();
assertTrue(rl.isLocked());
@@ -99,7 +99,7 @@ public class ReentrantLockTest extends JSR166TestCase {
/**
* Unlocking an unlocked lock throws IllegalMonitorStateException
*/
- public void testUnlock_IllegalMonitorStateException() {
+ public void testUnlock_IllegalMonitorStateException() {
ReentrantLock rl = new ReentrantLock();
try {
rl.unlock();
@@ -111,7 +111,7 @@ public class ReentrantLockTest extends JSR166TestCase {
/**
* tryLock on an unlocked lock succeeds
*/
- public void testTryLock() {
+ public void testTryLock() {
ReentrantLock rl = new ReentrantLock();
assertTrue(rl.tryLock());
assertTrue(rl.isLocked());
@@ -122,7 +122,7 @@ public class ReentrantLockTest extends JSR166TestCase {
/**
* hasQueuedThreads reports whether there are waiting threads
*/
- public void testhasQueuedThreads() {
+ public void testhasQueuedThreads() {
final ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(new InterruptedLockRunnable(lock));
Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
@@ -146,12 +146,12 @@ public class ReentrantLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* getQueueLength reports number of waiting threads
*/
- public void testGetQueueLength() {
+ public void testGetQueueLength() {
final ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(new InterruptedLockRunnable(lock));
Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
@@ -175,12 +175,12 @@ public class ReentrantLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* getQueueLength reports number of waiting threads
*/
- public void testGetQueueLength_fair() {
+ public void testGetQueueLength_fair() {
final ReentrantLock lock = new ReentrantLock(true);
Thread t1 = new Thread(new InterruptedLockRunnable(lock));
Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
@@ -204,12 +204,12 @@ public class ReentrantLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* hasQueuedThread(null) throws NPE
*/
- public void testHasQueuedThreadNPE() {
+ public void testHasQueuedThreadNPE() {
final ReentrantLock sync = new ReentrantLock();
try {
sync.hasQueuedThread(null);
@@ -221,7 +221,7 @@ public class ReentrantLockTest extends JSR166TestCase {
/**
* hasQueuedThread reports whether a thread is queued.
*/
- public void testHasQueuedThread() {
+ public void testHasQueuedThread() {
final ReentrantLock sync = new ReentrantLock();
Thread t1 = new Thread(new InterruptedLockRunnable(sync));
Thread t2 = new Thread(new InterruptibleLockRunnable(sync));
@@ -250,13 +250,13 @@ public class ReentrantLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* getQueuedThreads includes waiting threads
*/
- public void testGetQueuedThreads() {
+ public void testGetQueuedThreads() {
final PublicReentrantLock lock = new PublicReentrantLock();
Thread t1 = new Thread(new InterruptedLockRunnable(lock));
Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
@@ -283,13 +283,13 @@ public class ReentrantLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* timed tryLock is interruptible.
*/
- public void testInterruptedException2() {
+ public void testInterruptedException2() {
final ReentrantLock lock = new ReentrantLock();
lock.lock();
Thread t = new Thread(new Runnable() {
@@ -312,7 +312,7 @@ public class ReentrantLockTest extends JSR166TestCase {
/**
* TryLock on a locked lock fails
*/
- public void testTryLockWhenLocked() {
+ public void testTryLockWhenLocked() {
final ReentrantLock lock = new ReentrantLock();
lock.lock();
Thread t = new Thread(new Runnable() {
@@ -327,12 +327,12 @@ public class ReentrantLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* Timed tryLock on a locked lock times out
*/
- public void testTryLock_Timeout() {
+ public void testTryLock_Timeout() {
final ReentrantLock lock = new ReentrantLock();
lock.lock();
Thread t = new Thread(new Runnable() {
@@ -351,8 +351,8 @@ public class ReentrantLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
-
+ }
+
/**
* getHoldCount returns number of recursive holds
*/
@@ -367,8 +367,8 @@ public class ReentrantLockTest extends JSR166TestCase {
assertEquals(i-1,lock.getHoldCount());
}
}
-
-
+
+
/**
* isLocked is true when locked and false when not
*/
@@ -378,7 +378,7 @@ public class ReentrantLockTest extends JSR166TestCase {
assertTrue(lock.isLocked());
lock.unlock();
assertFalse(lock.isLocked());
- Thread t = new Thread(new Runnable() {
+ Thread t = new Thread(new Runnable() {
public void run() {
lock.lock();
try {
@@ -405,25 +405,27 @@ public class ReentrantLockTest extends JSR166TestCase {
/**
* lockInterruptibly is interruptible.
*/
- public void testLockInterruptibly1() {
+ public void testLockInterruptibly1() {
final ReentrantLock lock = new ReentrantLock();
lock.lock();
Thread t = new Thread(new InterruptedLockRunnable(lock));
try {
t.start();
+ Thread.sleep(SHORT_DELAY_MS);
t.interrupt();
+ Thread.sleep(SHORT_DELAY_MS);
lock.unlock();
t.join();
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* lockInterruptibly succeeds when unlocked, else is interruptible
*/
public void testLockInterruptibly2() {
- final ReentrantLock lock = new ReentrantLock();
+ final ReentrantLock lock = new ReentrantLock();
try {
lock.lockInterruptibly();
} catch(Exception e) {
@@ -445,7 +447,7 @@ public class ReentrantLockTest extends JSR166TestCase {
* Calling await without holding lock throws IllegalMonitorStateException
*/
public void testAwait_IllegalMonitor() {
- final ReentrantLock lock = new ReentrantLock();
+ final ReentrantLock lock = new ReentrantLock();
final Condition c = lock.newCondition();
try {
c.await();
@@ -462,7 +464,7 @@ public class ReentrantLockTest extends JSR166TestCase {
* Calling signal without holding lock throws IllegalMonitorStateException
*/
public void testSignal_IllegalMonitor() {
- final ReentrantLock lock = new ReentrantLock();
+ final ReentrantLock lock = new ReentrantLock();
final Condition c = lock.newCondition();
try {
c.signal();
@@ -479,7 +481,7 @@ public class ReentrantLockTest extends JSR166TestCase {
* awaitNanos without a signal times out
*/
public void testAwaitNanos_Timeout() {
- final ReentrantLock lock = new ReentrantLock();
+ final ReentrantLock lock = new ReentrantLock();
final Condition c = lock.newCondition();
try {
lock.lock();
@@ -496,11 +498,11 @@ public class ReentrantLockTest extends JSR166TestCase {
* timed await without a signal times out
*/
public void testAwait_Timeout() {
- final ReentrantLock lock = new ReentrantLock();
+ final ReentrantLock lock = new ReentrantLock();
final Condition c = lock.newCondition();
try {
lock.lock();
- assertFalse(c.await(SHORT_DELAY_MS, TimeUnit.MILLISECONDS));
+ c.await(SHORT_DELAY_MS, TimeUnit.MILLISECONDS);
lock.unlock();
}
catch (Exception ex) {
@@ -512,12 +514,12 @@ public class ReentrantLockTest extends JSR166TestCase {
* awaitUntil without a signal times out
*/
public void testAwaitUntil_Timeout() {
- final ReentrantLock lock = new ReentrantLock();
+ final ReentrantLock lock = new ReentrantLock();
final Condition c = lock.newCondition();
try {
lock.lock();
java.util.Date d = new java.util.Date();
- assertFalse(c.awaitUntil(new java.util.Date(d.getTime() + 10)));
+ c.awaitUntil(new java.util.Date(d.getTime() + 10));
lock.unlock();
}
catch (Exception ex) {
@@ -529,9 +531,9 @@ public class ReentrantLockTest extends JSR166TestCase {
* await returns when signalled
*/
public void testAwait() {
- final ReentrantLock lock = new ReentrantLock();
+ final ReentrantLock lock = new ReentrantLock();
final Condition c = lock.newCondition();
- Thread t = new Thread(new Runnable() {
+ Thread t = new Thread(new Runnable() {
public void run() {
try {
lock.lock();
@@ -670,9 +672,9 @@ public class ReentrantLockTest extends JSR166TestCase {
* getWaitingThreads throws IAE if not owned
*/
public void testGetWaitingThreadsIAE() {
- final PublicReentrantLock lock = new PublicReentrantLock();
+ final PublicReentrantLock lock = new PublicReentrantLock();
final Condition c = (lock.newCondition());
- final PublicReentrantLock lock2 = new PublicReentrantLock();
+ final PublicReentrantLock lock2 = new PublicReentrantLock();
try {
lock2.getWaitingThreads(c);
shouldThrow();
@@ -686,7 +688,7 @@ public class ReentrantLockTest extends JSR166TestCase {
* getWaitingThreads throws IMSE if not locked
*/
public void testGetWaitingThreadsIMSE() {
- final PublicReentrantLock lock = new PublicReentrantLock();
+ final PublicReentrantLock lock = new PublicReentrantLock();
final Condition c = (lock.newCondition());
try {
lock.getWaitingThreads(c);
@@ -703,9 +705,9 @@ public class ReentrantLockTest extends JSR166TestCase {
* hasWaiters returns true when a thread is waiting, else false
*/
public void testHasWaiters() {
- final ReentrantLock lock = new ReentrantLock();
+ final ReentrantLock lock = new ReentrantLock();
final Condition c = lock.newCondition();
- Thread t = new Thread(new Runnable() {
+ Thread t = new Thread(new Runnable() {
public void run() {
try {
lock.lock();
@@ -745,9 +747,9 @@ public class ReentrantLockTest extends JSR166TestCase {
* getWaitQueueLength returns number of waiting threads
*/
public void testGetWaitQueueLength() {
- final ReentrantLock lock = new ReentrantLock();
+ final ReentrantLock lock = new ReentrantLock();
final Condition c = lock.newCondition();
- Thread t1 = new Thread(new Runnable() {
+ Thread t1 = new Thread(new Runnable() {
public void run() {
try {
lock.lock();
@@ -762,7 +764,7 @@ public class ReentrantLockTest extends JSR166TestCase {
}
});
- Thread t2 = new Thread(new Runnable() {
+ Thread t2 = new Thread(new Runnable() {
public void run() {
try {
lock.lock();
@@ -806,9 +808,9 @@ public class ReentrantLockTest extends JSR166TestCase {
* getWaitingThreads returns only and all waiting threads
*/
public void testGetWaitingThreads() {
- final PublicReentrantLock lock = new PublicReentrantLock();
+ final PublicReentrantLock lock = new PublicReentrantLock();
final Condition c = lock.newCondition();
- Thread t1 = new Thread(new Runnable() {
+ Thread t1 = new Thread(new Runnable() {
public void run() {
try {
lock.lock();
@@ -822,7 +824,7 @@ public class ReentrantLockTest extends JSR166TestCase {
}
});
- Thread t2 = new Thread(new Runnable() {
+ Thread t2 = new Thread(new Runnable() {
public void run() {
try {
lock.lock();
@@ -865,34 +867,61 @@ public class ReentrantLockTest extends JSR166TestCase {
}
}
+ /** A helper class for uninterruptible wait tests */
+ class UninterruptableThread extends Thread {
+ private ReentrantLock lock;
+ private Condition c;
+
+ public volatile boolean canAwake = false;
+ public volatile boolean interrupted = false;
+ public volatile boolean lockStarted = false;
+
+ public UninterruptableThread(ReentrantLock lock, Condition c) {
+ this.lock = lock;
+ this.c = c;
+ }
+
+ public synchronized void run() {
+ lock.lock();
+ lockStarted = true;
+ while (!canAwake) {
+ c.awaitUninterruptibly();
+ }
+
+ interrupted = isInterrupted();
+ lock.unlock();
+ }
+ }
/**
* awaitUninterruptibly doesn't abort on interrupt
*/
public void testAwaitUninterruptibly() {
- final ReentrantLock lock = new ReentrantLock();
+ final ReentrantLock lock = new ReentrantLock();
final Condition c = lock.newCondition();
- Thread t = new Thread(new Runnable() {
- public void run() {
- lock.lock();
- c.awaitUninterruptibly();
- lock.unlock();
- }
- });
+ UninterruptableThread thread = new UninterruptableThread(lock, c);
try {
- t.start();
- Thread.sleep(SHORT_DELAY_MS);
- t.interrupt();
+ thread.start();
+
+ while (!thread.lockStarted) {
+ Thread.sleep(100);
+ }
+
lock.lock();
- c.signal();
- lock.unlock();
- assert(t.isInterrupted());
- t.join(SHORT_DELAY_MS);
- assertFalse(t.isAlive());
- }
- catch (Exception ex) {
+ try {
+ thread.interrupt();
+ thread.canAwake = true;
+ c.signal();
+ } finally {
+ lock.unlock();
+ }
+
+ thread.join();
+ assertTrue(thread.interrupted);
+ assertFalse(thread.isAlive());
+ } catch (Exception ex) {
unexpectedException();
}
}
@@ -901,9 +930,9 @@ public class ReentrantLockTest extends JSR166TestCase {
* await is interruptible
*/
public void testAwait_Interrupt() {
- final ReentrantLock lock = new ReentrantLock();
+ final ReentrantLock lock = new ReentrantLock();
final Condition c = lock.newCondition();
- Thread t = new Thread(new Runnable() {
+ Thread t = new Thread(new Runnable() {
public void run() {
try {
lock.lock();
@@ -932,9 +961,9 @@ public class ReentrantLockTest extends JSR166TestCase {
* awaitNanos is interruptible
*/
public void testAwaitNanos_Interrupt() {
- final ReentrantLock lock = new ReentrantLock();
+ final ReentrantLock lock = new ReentrantLock();
final Condition c = lock.newCondition();
- Thread t = new Thread(new Runnable() {
+ Thread t = new Thread(new Runnable() {
public void run() {
try {
lock.lock();
@@ -963,9 +992,9 @@ public class ReentrantLockTest extends JSR166TestCase {
* awaitUntil is interruptible
*/
public void testAwaitUntil_Interrupt() {
- final ReentrantLock lock = new ReentrantLock();
+ final ReentrantLock lock = new ReentrantLock();
final Condition c = lock.newCondition();
- Thread t = new Thread(new Runnable() {
+ Thread t = new Thread(new Runnable() {
public void run() {
try {
lock.lock();
@@ -995,9 +1024,9 @@ public class ReentrantLockTest extends JSR166TestCase {
* signalAll wakes up all threads
*/
public void testSignalAll() {
- final ReentrantLock lock = new ReentrantLock();
+ final ReentrantLock lock = new ReentrantLock();
final Condition c = lock.newCondition();
- Thread t1 = new Thread(new Runnable() {
+ Thread t1 = new Thread(new Runnable() {
public void run() {
try {
lock.lock();
@@ -1010,7 +1039,7 @@ public class ReentrantLockTest extends JSR166TestCase {
}
});
- Thread t2 = new Thread(new Runnable() {
+ Thread t2 = new Thread(new Runnable() {
public void run() {
try {
lock.lock();
@@ -1041,6 +1070,61 @@ public class ReentrantLockTest extends JSR166TestCase {
}
/**
+ * await after multiple reentrant locking preserves lock count
+ */
+ public void testAwaitLockCount() {
+ final ReentrantLock lock = new ReentrantLock();
+ final Condition c = lock.newCondition();
+ Thread t1 = new Thread(new Runnable() {
+ public void run() {
+ try {
+ lock.lock();
+ threadAssertEquals(1, lock.getHoldCount());
+ c.await();
+ threadAssertEquals(1, lock.getHoldCount());
+ lock.unlock();
+ }
+ catch(InterruptedException e) {
+ threadUnexpectedException();
+ }
+ }
+ });
+
+ Thread t2 = new Thread(new Runnable() {
+ public void run() {
+ try {
+ lock.lock();
+ lock.lock();
+ threadAssertEquals(2, lock.getHoldCount());
+ c.await();
+ threadAssertEquals(2, lock.getHoldCount());
+ lock.unlock();
+ lock.unlock();
+ }
+ catch(InterruptedException e) {
+ threadUnexpectedException();
+ }
+ }
+ });
+
+ try {
+ t1.start();
+ t2.start();
+ Thread.sleep(SHORT_DELAY_MS);
+ lock.lock();
+ c.signalAll();
+ lock.unlock();
+ t1.join(SHORT_DELAY_MS);
+ t2.join(SHORT_DELAY_MS);
+ assertFalse(t1.isAlive());
+ assertFalse(t2.isAlive());
+ }
+ catch (Exception ex) {
+ unexpectedException();
+ }
+ }
+
+ /**
* A serialized lock deserializes as unlocked
*/
public void testSerialization() {
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantReadWriteLockTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantReadWriteLockTest.java
index 8925b42..e38165a 100644
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantReadWriteLockTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantReadWriteLockTest.java
@@ -16,7 +16,7 @@ import java.util.*;
public class ReentrantReadWriteLockTest extends JSR166TestCase {
public static void main(String[] args) {
- junit.textui.TestRunner.run (suite());
+ junit.textui.TestRunner.run (suite());
}
public static Test suite() {
return new TestSuite(ReentrantReadWriteLockTest.class);
@@ -56,18 +56,18 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
*/
static class PublicReentrantReadWriteLock extends ReentrantReadWriteLock {
PublicReentrantReadWriteLock() { super(); }
- public Collection<Thread> getQueuedThreads() {
- return super.getQueuedThreads();
+ public Collection<Thread> getQueuedThreads() {
+ return super.getQueuedThreads();
}
- public Collection<Thread> getWaitingThreads(Condition c) {
- return super.getWaitingThreads(c);
+ public Collection<Thread> getWaitingThreads(Condition c) {
+ return super.getWaitingThreads(c);
}
}
/**
* Constructor sets given fairness, and is in unlocked state
*/
- public void testConstructor() {
+ public void testConstructor() {
ReentrantReadWriteLock rl = new ReentrantReadWriteLock();
assertFalse(rl.isFair());
assertFalse(rl.isWriteLocked());
@@ -81,7 +81,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
/**
* write-locking and read-locking an unlocked lock succeed
*/
- public void testLock() {
+ public void testLock() {
ReentrantReadWriteLock rl = new ReentrantReadWriteLock();
rl.writeLock().lock();
assertTrue(rl.isWriteLocked());
@@ -105,7 +105,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
/**
* locking an unlocked fair lock succeeds
*/
- public void testFairLock() {
+ public void testFairLock() {
ReentrantReadWriteLock rl = new ReentrantReadWriteLock(true);
rl.writeLock().lock();
assertTrue(rl.isWriteLocked());
@@ -128,23 +128,23 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
/**
* getWriteHoldCount returns number of recursive holds
*/
- public void testGetHoldCount() {
- ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
- for(int i = 1; i <= SIZE; i++) {
- lock.writeLock().lock();
- assertEquals(i,lock.getWriteHoldCount());
- }
- for(int i = SIZE; i > 0; i--) {
- lock.writeLock().unlock();
- assertEquals(i-1,lock.getWriteHoldCount());
- }
+ public void testGetWriteHoldCount() {
+ ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ for(int i = 1; i <= SIZE; i++) {
+ lock.writeLock().lock();
+ assertEquals(i,lock.getWriteHoldCount());
+ }
+ for(int i = SIZE; i > 0; i--) {
+ lock.writeLock().unlock();
+ assertEquals(i-1,lock.getWriteHoldCount());
+ }
}
-
+
/**
* write-unlocking an unlocked lock throws IllegalMonitorStateException
*/
- public void testUnlock_IllegalMonitorStateException() {
+ public void testUnlock_IllegalMonitorStateException() {
ReentrantReadWriteLock rl = new ReentrantReadWriteLock();
try {
rl.writeLock().unlock();
@@ -156,7 +156,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
/**
* write-lockInterruptibly is interruptible
*/
- public void testWriteLockInterruptibly_Interrupted() {
+ public void testWriteLockInterruptibly_Interrupted() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
Thread t = new Thread(new Runnable() {
public void run() {
@@ -171,18 +171,20 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
try {
lock.writeLock().lock();
t.start();
+ Thread.sleep(SHORT_DELAY_MS);
t.interrupt();
+ Thread.sleep(SHORT_DELAY_MS);
lock.writeLock().unlock();
t.join();
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* timed write-tryLock is interruptible
*/
- public void testWriteTryLock_Interrupted() {
+ public void testWriteTryLock_Interrupted() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.writeLock().lock();
Thread t = new Thread(new Runnable() {
@@ -205,7 +207,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
/**
* read-lockInterruptibly is interruptible
*/
- public void testReadLockInterruptibly_Interrupted() {
+ public void testReadLockInterruptibly_Interrupted() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.writeLock().lock();
Thread t = new Thread(new Runnable() {
@@ -217,18 +219,20 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
});
try {
t.start();
+ Thread.sleep(SHORT_DELAY_MS);
t.interrupt();
+ Thread.sleep(SHORT_DELAY_MS);
lock.writeLock().unlock();
t.join();
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* timed read-tryLock is interruptible
*/
- public void testReadTryLock_Interrupted() {
+ public void testReadTryLock_Interrupted() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.writeLock().lock();
Thread t = new Thread(new Runnable() {
@@ -248,11 +252,11 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
}
}
-
+
/**
* write-tryLock fails if locked
*/
- public void testWriteTryLockWhenLocked() {
+ public void testWriteTryLockWhenLocked() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.writeLock().lock();
Thread t = new Thread(new Runnable() {
@@ -267,12 +271,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* read-tryLock fails if locked
*/
- public void testReadTryLockWhenLocked() {
+ public void testReadTryLockWhenLocked() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.writeLock().lock();
Thread t = new Thread(new Runnable() {
@@ -287,12 +291,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* Multiple threads can hold a read lock when not write-locked
*/
- public void testMultipleReadLocks() {
+ public void testMultipleReadLocks() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.readLock().lock();
Thread t = new Thread(new Runnable() {
@@ -308,12 +312,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* A writelock succeeds after reading threads unlock
*/
- public void testWriteAfterMultipleReadLocks() {
+ public void testWriteAfterMultipleReadLocks() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.readLock().lock();
Thread t1 = new Thread(new Runnable() {
@@ -338,16 +342,16 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
t2.join(MEDIUM_DELAY_MS);
assertTrue(!t1.isAlive());
assertTrue(!t2.isAlive());
-
+
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* Readlocks succeed after a writing thread unlocks
*/
- public void testReadAfterWriteLock() {
+ public void testReadAfterWriteLock() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.writeLock().lock();
Thread t1 = new Thread(new Runnable() {
@@ -372,17 +376,280 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
t2.join(MEDIUM_DELAY_MS);
assertTrue(!t1.isAlive());
assertTrue(!t2.isAlive());
-
+
+ } catch(Exception e){
+ unexpectedException();
+ }
+ }
+
+ /**
+ * Read trylock succeeds if write locked by current thread
+ */
+ public void testReadHoldingWriteLock() {
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ lock.writeLock().lock();
+ assertTrue(lock.readLock().tryLock());
+ lock.readLock().unlock();
+ lock.writeLock().unlock();
+ }
+
+ /**
+ * Read lock succeeds if write locked by current thread even if
+ * other threads are waiting for readlock
+ */
+ public void testReadHoldingWriteLock2() {
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ lock.writeLock().lock();
+ Thread t1 = new Thread(new Runnable() {
+ public void run() {
+ lock.readLock().lock();
+ lock.readLock().unlock();
+ }
+ });
+ Thread t2 = new Thread(new Runnable() {
+ public void run() {
+ lock.readLock().lock();
+ lock.readLock().unlock();
+ }
+ });
+
+ try {
+ t1.start();
+ t2.start();
+ lock.readLock().lock();
+ lock.readLock().unlock();
+ Thread.sleep(SHORT_DELAY_MS);
+ lock.readLock().lock();
+ lock.readLock().unlock();
+ lock.writeLock().unlock();
+ t1.join(MEDIUM_DELAY_MS);
+ t2.join(MEDIUM_DELAY_MS);
+ assertTrue(!t1.isAlive());
+ assertTrue(!t2.isAlive());
+
+ } catch(Exception e){
+ unexpectedException();
+ }
+ }
+
+ /**
+ * Read lock succeeds if write locked by current thread even if
+ * other threads are waiting for writelock
+ */
+ public void testReadHoldingWriteLock3() {
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ lock.writeLock().lock();
+ Thread t1 = new Thread(new Runnable() {
+ public void run() {
+ lock.writeLock().lock();
+ lock.writeLock().unlock();
+ }
+ });
+ Thread t2 = new Thread(new Runnable() {
+ public void run() {
+ lock.writeLock().lock();
+ lock.writeLock().unlock();
+ }
+ });
+
+ try {
+ t1.start();
+ t2.start();
+ lock.readLock().lock();
+ lock.readLock().unlock();
+ Thread.sleep(SHORT_DELAY_MS);
+ lock.readLock().lock();
+ lock.readLock().unlock();
+ lock.writeLock().unlock();
+ t1.join(MEDIUM_DELAY_MS);
+ t2.join(MEDIUM_DELAY_MS);
+ assertTrue(!t1.isAlive());
+ assertTrue(!t2.isAlive());
+
+ } catch(Exception e){
+ unexpectedException();
+ }
+ }
+
+
+ /**
+ * Write lock succeeds if write locked by current thread even if
+ * other threads are waiting for writelock
+ */
+ public void testWriteHoldingWriteLock4() {
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ lock.writeLock().lock();
+ Thread t1 = new Thread(new Runnable() {
+ public void run() {
+ lock.writeLock().lock();
+ lock.writeLock().unlock();
+ }
+ });
+ Thread t2 = new Thread(new Runnable() {
+ public void run() {
+ lock.writeLock().lock();
+ lock.writeLock().unlock();
+ }
+ });
+
+ try {
+ t1.start();
+ t2.start();
+ lock.writeLock().lock();
+ lock.writeLock().unlock();
+ Thread.sleep(SHORT_DELAY_MS);
+ lock.writeLock().lock();
+ lock.writeLock().unlock();
+ lock.writeLock().unlock();
+ t1.join(MEDIUM_DELAY_MS);
+ t2.join(MEDIUM_DELAY_MS);
+ assertTrue(!t1.isAlive());
+ assertTrue(!t2.isAlive());
+
+ } catch(Exception e){
+ unexpectedException();
+ }
+ }
+
+
+ /**
+ * Fair Read trylock succeeds if write locked by current thread
+ */
+ public void testReadHoldingWriteLockFair() {
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
+ lock.writeLock().lock();
+ assertTrue(lock.readLock().tryLock());
+ lock.readLock().unlock();
+ lock.writeLock().unlock();
+ }
+
+ /**
+ * Fair Read lock succeeds if write locked by current thread even if
+ * other threads are waiting for readlock
+ */
+ public void testReadHoldingWriteLockFair2() {
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
+ lock.writeLock().lock();
+ Thread t1 = new Thread(new Runnable() {
+ public void run() {
+ lock.readLock().lock();
+ lock.readLock().unlock();
+ }
+ });
+ Thread t2 = new Thread(new Runnable() {
+ public void run() {
+ lock.readLock().lock();
+ lock.readLock().unlock();
+ }
+ });
+
+ try {
+ t1.start();
+ t2.start();
+ lock.readLock().lock();
+ lock.readLock().unlock();
+ Thread.sleep(SHORT_DELAY_MS);
+ lock.readLock().lock();
+ lock.readLock().unlock();
+ lock.writeLock().unlock();
+ t1.join(MEDIUM_DELAY_MS);
+ t2.join(MEDIUM_DELAY_MS);
+ assertTrue(!t1.isAlive());
+ assertTrue(!t2.isAlive());
+
+ } catch(Exception e){
+ unexpectedException();
+ }
+ }
+
+
+ /**
+ * Fair Read lock succeeds if write locked by current thread even if
+ * other threads are waiting for writelock
+ */
+ public void testReadHoldingWriteLockFair3() {
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
+ lock.writeLock().lock();
+ Thread t1 = new Thread(new Runnable() {
+ public void run() {
+ lock.writeLock().lock();
+ lock.writeLock().unlock();
+ }
+ });
+ Thread t2 = new Thread(new Runnable() {
+ public void run() {
+ lock.writeLock().lock();
+ lock.writeLock().unlock();
+ }
+ });
+
+ try {
+ t1.start();
+ t2.start();
+ lock.readLock().lock();
+ lock.readLock().unlock();
+ Thread.sleep(SHORT_DELAY_MS);
+ lock.readLock().lock();
+ lock.readLock().unlock();
+ lock.writeLock().unlock();
+ t1.join(MEDIUM_DELAY_MS);
+ t2.join(MEDIUM_DELAY_MS);
+ assertTrue(!t1.isAlive());
+ assertTrue(!t2.isAlive());
+
} catch(Exception e){
unexpectedException();
}
- }
+ }
+
+
+ /**
+ * Fair Write lock succeeds if write locked by current thread even if
+ * other threads are waiting for writelock
+ */
+ public void testWriteHoldingWriteLockFair4() {
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
+ lock.writeLock().lock();
+ Thread t1 = new Thread(new Runnable() {
+ public void run() {
+ lock.writeLock().lock();
+ lock.writeLock().unlock();
+ }
+ });
+ Thread t2 = new Thread(new Runnable() {
+ public void run() {
+ lock.writeLock().lock();
+ lock.writeLock().unlock();
+ }
+ });
+
+ try {
+ t1.start();
+ t2.start();
+ Thread.sleep(SHORT_DELAY_MS);
+ assertTrue(lock.isWriteLockedByCurrentThread());
+ assertTrue(lock.getWriteHoldCount() == 1);
+ lock.writeLock().lock();
+ assertTrue(lock.getWriteHoldCount() == 2);
+ lock.writeLock().unlock();
+ lock.writeLock().lock();
+ lock.writeLock().unlock();
+ lock.writeLock().unlock();
+ t1.join(MEDIUM_DELAY_MS);
+ t2.join(MEDIUM_DELAY_MS);
+ assertTrue(!t1.isAlive());
+ assertTrue(!t2.isAlive());
+
+ } catch(Exception e){
+ unexpectedException();
+ }
+ }
/**
* Read tryLock succeeds if readlocked but not writelocked
*/
- public void testTryLockWhenReadLocked() {
+ public void testTryLockWhenReadLocked() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.readLock().lock();
Thread t = new Thread(new Runnable() {
@@ -398,14 +665,14 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
+
-
/**
* write tryLock fails when readlocked
*/
- public void testWriteTryLockWhenReadLocked() {
+ public void testWriteTryLockWhenReadLocked() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.readLock().lock();
Thread t = new Thread(new Runnable() {
@@ -420,14 +687,58 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
+
+
+ /**
+ * Fair Read tryLock succeeds if readlocked but not writelocked
+ */
+ public void testTryLockWhenReadLockedFair() {
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
+ lock.readLock().lock();
+ Thread t = new Thread(new Runnable() {
+ public void run() {
+ threadAssertTrue(lock.readLock().tryLock());
+ lock.readLock().unlock();
+ }
+ });
+ try {
+ t.start();
+ t.join();
+ lock.readLock().unlock();
+ } catch(Exception e){
+ unexpectedException();
+ }
+ }
+
+
+
+ /**
+ * Fair write tryLock fails when readlocked
+ */
+ public void testWriteTryLockWhenReadLockedFair() {
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
+ lock.readLock().lock();
+ Thread t = new Thread(new Runnable() {
+ public void run() {
+ threadAssertFalse(lock.writeLock().tryLock());
+ }
+ });
+ try {
+ t.start();
+ t.join();
+ lock.readLock().unlock();
+ } catch(Exception e){
+ unexpectedException();
+ }
+ }
+
-
/**
* write timed tryLock times out if locked
*/
- public void testWriteTryLock_Timeout() {
+ public void testWriteTryLock_Timeout() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.writeLock().lock();
Thread t = new Thread(new Runnable() {
@@ -446,12 +757,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* read timed tryLock times out if write-locked
*/
- public void testReadTryLock_Timeout() {
+ public void testReadTryLock_Timeout() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.writeLock().lock();
Thread t = new Thread(new Runnable() {
@@ -470,7 +781,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
@@ -483,7 +794,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
} catch(Exception e) {
unexpectedException();
}
- Thread t = new Thread(new Runnable() {
+ Thread t = new Thread(new Runnable() {
public void run() {
try {
lock.writeLock().lockInterruptibly();
@@ -495,7 +806,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
});
try {
t.start();
+ Thread.sleep(SHORT_DELAY_MS);
t.interrupt();
+ Thread.sleep(SHORT_DELAY_MS);
t.join();
lock.writeLock().unlock();
} catch(Exception e){
@@ -513,7 +826,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
} catch(Exception e) {
unexpectedException();
}
- Thread t = new Thread(new Runnable() {
+ Thread t = new Thread(new Runnable() {
public void run() {
try {
lock.readLock().lockInterruptibly();
@@ -525,6 +838,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
});
try {
t.start();
+ Thread.sleep(SHORT_DELAY_MS);
t.interrupt();
t.join();
lock.writeLock().unlock();
@@ -537,7 +851,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
* Calling await without holding lock throws IllegalMonitorStateException
*/
public void testAwait_IllegalMonitor() {
- final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
final Condition c = lock.writeLock().newCondition();
try {
c.await();
@@ -554,7 +868,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
* Calling signal without holding lock throws IllegalMonitorStateException
*/
public void testSignal_IllegalMonitor() {
- final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
final Condition c = lock.writeLock().newCondition();
try {
c.signal();
@@ -571,7 +885,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
* awaitNanos without a signal times out
*/
public void testAwaitNanos_Timeout() {
- final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
final Condition c = lock.writeLock().newCondition();
try {
lock.writeLock().lock();
@@ -589,11 +903,10 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
* timed await without a signal times out
*/
public void testAwait_Timeout() {
- final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
final Condition c = lock.writeLock().newCondition();
try {
lock.writeLock().lock();
- assertFalse(c.await(10, TimeUnit.MILLISECONDS));
lock.writeLock().unlock();
}
catch (Exception ex) {
@@ -605,12 +918,11 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
* awaitUntil without a signal times out
*/
public void testAwaitUntil_Timeout() {
- final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
final Condition c = lock.writeLock().newCondition();
try {
lock.writeLock().lock();
java.util.Date d = new java.util.Date();
- assertFalse(c.awaitUntil(new java.util.Date(d.getTime() + 10)));
lock.writeLock().unlock();
}
catch (Exception ex) {
@@ -622,9 +934,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
* await returns when signalled
*/
public void testAwait() {
- final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
final Condition c = lock.writeLock().newCondition();
- Thread t = new Thread(new Runnable() {
+ Thread t = new Thread(new Runnable() {
public void run() {
try {
lock.writeLock().lock();
@@ -651,32 +963,61 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
}
}
+ /** A helper class for uninterruptible wait tests */
+ class UninterruptableThread extends Thread {
+ private Lock lock;
+ private Condition c;
+
+ public volatile boolean canAwake = false;
+ public volatile boolean interrupted = false;
+ public volatile boolean lockStarted = false;
+
+ public UninterruptableThread(Lock lock, Condition c) {
+ this.lock = lock;
+ this.c = c;
+ }
+
+ public synchronized void run() {
+ lock.lock();
+ lockStarted = true;
+
+ while (!canAwake) {
+ c.awaitUninterruptibly();
+ }
+
+ interrupted = isInterrupted();
+ lock.unlock();
+ }
+ }
+
/**
* awaitUninterruptibly doesn't abort on interrupt
*/
public void testAwaitUninterruptibly() {
- final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
final Condition c = lock.writeLock().newCondition();
- Thread t = new Thread(new Runnable() {
- public void run() {
- lock.writeLock().lock();
- c.awaitUninterruptibly();
- lock.writeLock().unlock();
- }
- });
+ UninterruptableThread thread = new UninterruptableThread(lock.writeLock(), c);
try {
- t.start();
- Thread.sleep(SHORT_DELAY_MS);
- t.interrupt();
+ thread.start();
+
+ while (!thread.lockStarted) {
+ Thread.sleep(100);
+ }
+
lock.writeLock().lock();
- c.signal();
- lock.writeLock().unlock();
- assert(t.isInterrupted());
- t.join(SHORT_DELAY_MS);
- assertFalse(t.isAlive());
- }
- catch (Exception ex) {
+ try {
+ thread.interrupt();
+ thread.canAwake = true;
+ c.signal();
+ } finally {
+ lock.writeLock().unlock();
+ }
+
+ thread.join();
+ assertTrue(thread.interrupted);
+ assertFalse(thread.isAlive());
+ } catch (Exception ex) {
unexpectedException();
}
}
@@ -685,9 +1026,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
* await is interruptible
*/
public void testAwait_Interrupt() {
- final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
final Condition c = lock.writeLock().newCondition();
- Thread t = new Thread(new Runnable() {
+ Thread t = new Thread(new Runnable() {
public void run() {
try {
lock.writeLock().lock();
@@ -716,9 +1057,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
* awaitNanos is interruptible
*/
public void testAwaitNanos_Interrupt() {
- final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
final Condition c = lock.writeLock().newCondition();
- Thread t = new Thread(new Runnable() {
+ Thread t = new Thread(new Runnable() {
public void run() {
try {
lock.writeLock().lock();
@@ -747,9 +1088,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
* awaitUntil is interruptible
*/
public void testAwaitUntil_Interrupt() {
- final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
final Condition c = lock.writeLock().newCondition();
- Thread t = new Thread(new Runnable() {
+ Thread t = new Thread(new Runnable() {
public void run() {
try {
lock.writeLock().lock();
@@ -779,9 +1120,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
* signalAll wakes up all threads
*/
public void testSignalAll() {
- final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
final Condition c = lock.writeLock().newCondition();
- Thread t1 = new Thread(new Runnable() {
+ Thread t1 = new Thread(new Runnable() {
public void run() {
try {
lock.writeLock().lock();
@@ -794,7 +1135,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
}
});
- Thread t2 = new Thread(new Runnable() {
+ Thread t2 = new Thread(new Runnable() {
public void run() {
try {
lock.writeLock().lock();
@@ -852,7 +1193,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
/**
* hasQueuedThreads reports whether there are waiting threads
*/
- public void testhasQueuedThreads() {
+ public void testhasQueuedThreads() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
Thread t1 = new Thread(new InterruptedLockRunnable(lock));
Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
@@ -876,12 +1217,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* hasQueuedThread(null) throws NPE
*/
- public void testHasQueuedThreadNPE() {
+ public void testHasQueuedThreadNPE() {
final ReentrantReadWriteLock sync = new ReentrantReadWriteLock();
try {
sync.hasQueuedThread(null);
@@ -893,7 +1234,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
/**
* hasQueuedThread reports whether a thread is queued.
*/
- public void testHasQueuedThread() {
+ public void testHasQueuedThread() {
final ReentrantReadWriteLock sync = new ReentrantReadWriteLock();
Thread t1 = new Thread(new InterruptedLockRunnable(sync));
Thread t2 = new Thread(new InterruptibleLockRunnable(sync));
@@ -922,13 +1263,13 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* getQueueLength reports number of waiting threads
*/
- public void testGetQueueLength() {
+ public void testGetQueueLength() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
Thread t1 = new Thread(new InterruptedLockRunnable(lock));
Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
@@ -952,12 +1293,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* getQueuedThreads includes waiting threads
*/
- public void testGetQueuedThreads() {
+ public void testGetQueuedThreads() {
final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock();
Thread t1 = new Thread(new InterruptedLockRunnable(lock));
Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
@@ -984,7 +1325,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
} catch(Exception e){
unexpectedException();
}
- }
+ }
/**
* hasWaiters throws NPE if null
@@ -1097,9 +1438,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
* getWaitingThreads throws IAE if not owned
*/
public void testGetWaitingThreadsIAE() {
- final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock();
+ final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock();
final Condition c = (lock.writeLock().newCondition());
- final PublicReentrantReadWriteLock lock2 = new PublicReentrantReadWriteLock();
+ final PublicReentrantReadWriteLock lock2 = new PublicReentrantReadWriteLock();
try {
lock2.getWaitingThreads(c);
shouldThrow();
@@ -1113,7 +1454,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
* getWaitingThreads throws IMSE if not locked
*/
public void testGetWaitingThreadsIMSE() {
- final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock();
+ final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock();
final Condition c = (lock.writeLock().newCondition());
try {
lock.getWaitingThreads(c);
@@ -1131,7 +1472,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
public void testHasWaiters() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
final Condition c = (lock.writeLock().newCondition());
- Thread t = new Thread(new Runnable() {
+ Thread t = new Thread(new Runnable() {
public void run() {
try {
lock.writeLock().lock();
@@ -1173,7 +1514,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
public void testGetWaitQueueLength() {
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
final Condition c = (lock.writeLock().newCondition());
- Thread t = new Thread(new Runnable() {
+ Thread t = new Thread(new Runnable() {
public void run() {
try {
lock.writeLock().lock();
@@ -1214,9 +1555,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
* getWaitingThreads returns only and all waiting threads
*/
public void testGetWaitingThreads() {
- final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock();
+ final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock();
final Condition c = lock.writeLock().newCondition();
- Thread t1 = new Thread(new Runnable() {
+ Thread t1 = new Thread(new Runnable() {
public void run() {
try {
lock.writeLock().lock();
@@ -1230,7 +1571,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
}
});
- Thread t2 = new Thread(new Runnable() {
+ Thread t2 = new Thread(new Runnable() {
public void run() {
try {
lock.writeLock().lock();
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/SynchronousQueueTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/SynchronousQueueTest.java
index 19bdede..debce5d 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/SynchronousQueueTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/SynchronousQueueTest.java
@@ -757,6 +757,7 @@ public class SynchronousQueueTest extends JSR166TestCase {
while (!q.isEmpty())
assertEquals(q.remove(), r.remove());
} catch(Exception e){
+ e.printStackTrace();
unexpectedException();
}
}
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/ThreadPoolExecutorTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/ThreadPoolExecutorTest.java
index ca4ba78..4f7cc46 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/ThreadPoolExecutorTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/ThreadPoolExecutorTest.java
@@ -9,6 +9,7 @@
package tests.api.java.util.concurrent;
import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
import junit.framework.*;
import java.util.*;
@@ -38,6 +39,15 @@ public class ThreadPoolExecutorTest extends JSR166TestCase {
}
}
+ static class FailingThreadFactory implements ThreadFactory{
+ int calls = 0;
+ public Thread newThread(Runnable r){
+ if (++calls > 1) return null;
+ return new Thread(r);
+ }
+ }
+
+
/**
* execute successfully executes a runnable
*/
@@ -342,6 +352,9 @@ public class ThreadPoolExecutorTest extends JSR166TestCase {
assertSame(q, wq);
assertFalse(wq.contains(tasks[0]));
assertTrue(wq.contains(tasks[4]));
+ for (int i = 1; i < 5; ++i)
+ tasks[i].cancel(true);
+ p1.shutdownNow();
} catch(Exception e) {
unexpectedException();
} finally {
@@ -1500,5 +1513,58 @@ public class ThreadPoolExecutorTest extends JSR166TestCase {
}
}
+ /**
+ * Execution continues if there is at least one thread even if
+ * thread factory fails to create more
+ */
+ public void testFailingThreadFactory() {
+ ExecutorService e = new ThreadPoolExecutor(100, 100, LONG_DELAY_MS, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new FailingThreadFactory());
+ try {
+ ArrayList<Callable<String>> l = new ArrayList<Callable<String>>();
+ for (int k = 0; k < 100; ++k) {
+ e.execute(new NoOpRunnable());
+ }
+ Thread.sleep(LONG_DELAY_MS);
+ } catch(Exception ex) {
+ unexpectedException();
+ } finally {
+ joinPool(e);
+ }
+ }
+ /**
+ * execute allows the same task to be submitted multiple times, even
+ * if rejected
+ */
+ public void testRejectedRecycledTask() {
+ final int nTasks = 1000;
+ final AtomicInteger nRun = new AtomicInteger(0);
+ final Runnable recycledTask = new Runnable() {
+ public void run() {
+ nRun.getAndIncrement();
+ } };
+ final ThreadPoolExecutor p =
+ new ThreadPoolExecutor(1, 30, 60, TimeUnit.SECONDS,
+ new ArrayBlockingQueue(30));
+ try {
+ for (int i = 0; i < nTasks; ++i) {
+ for (;;) {
+ try {
+ p.execute(recycledTask);
+ break;
+ }
+ catch (RejectedExecutionException ignore) {
+ }
+ }
+ }
+ Thread.sleep(5000); // enough time to run all tasks
+ assertEquals(nRun.get(), nTasks);
+ } catch(Exception ex) {
+ ex.printStackTrace();
+ unexpectedException();
+ } finally {
+ p.shutdown();
+ }
+ }
+
}
diff --git a/concurrent/src/test/java/tests/api/java/util/concurrent/TimeUnitTest.java b/concurrent/src/test/java/tests/api/java/util/concurrent/TimeUnitTest.java
index 1a9a04a..54fdc69 100755
--- a/concurrent/src/test/java/tests/api/java/util/concurrent/TimeUnitTest.java
+++ b/concurrent/src/test/java/tests/api/java/util/concurrent/TimeUnitTest.java
@@ -21,54 +21,60 @@ public class TimeUnitTest extends JSR166TestCase {
return new TestSuite(TimeUnitTest.class);
}
+ // (loops to 88888 check increments at all time divisions.)
+
/**
- * convert correctly converts sample values across the four units
+ * convert correctly converts sample values across the units
*/
public void testConvert() {
- for (long t = 0; t < 10; ++t) {
- assertEquals(t,
+ for (long t = 0; t < 88888; ++t) {
+ assertEquals(t,
TimeUnit.SECONDS.convert(t,
TimeUnit.SECONDS));
assertEquals(t,
- TimeUnit.SECONDS.convert(1000 * t,
+ TimeUnit.SECONDS.convert(1000L*t,
TimeUnit.MILLISECONDS));
assertEquals(t,
- TimeUnit.SECONDS.convert(1000000 * t,
+ TimeUnit.SECONDS.convert(1000000L*t,
TimeUnit.MICROSECONDS));
assertEquals(t,
- TimeUnit.SECONDS.convert(1000000000 * t,
+ TimeUnit.SECONDS.convert(1000000000L*t,
TimeUnit.NANOSECONDS));
- assertEquals(1000 * t,
+
+
+ assertEquals(1000L*t,
TimeUnit.MILLISECONDS.convert(t,
TimeUnit.SECONDS));
assertEquals(t,
TimeUnit.MILLISECONDS.convert(t,
TimeUnit.MILLISECONDS));
assertEquals(t,
- TimeUnit.MILLISECONDS.convert(1000 * t,
+ TimeUnit.MILLISECONDS.convert(1000L*t,
TimeUnit.MICROSECONDS));
assertEquals(t,
- TimeUnit.MILLISECONDS.convert(1000000 * t,
+ TimeUnit.MILLISECONDS.convert(1000000L*t,
TimeUnit.NANOSECONDS));
- assertEquals(1000000 * t,
+
+ assertEquals(1000000L*t,
TimeUnit.MICROSECONDS.convert(t,
TimeUnit.SECONDS));
- assertEquals(1000 * t,
+ assertEquals(1000L*t,
TimeUnit.MICROSECONDS.convert(t,
TimeUnit.MILLISECONDS));
assertEquals(t,
TimeUnit.MICROSECONDS.convert(t,
TimeUnit.MICROSECONDS));
assertEquals(t,
- TimeUnit.MICROSECONDS.convert(1000 * t,
+ TimeUnit.MICROSECONDS.convert(1000L*t,
TimeUnit.NANOSECONDS));
- assertEquals(1000000000 * t,
+
+ assertEquals(1000000000L*t,
TimeUnit.NANOSECONDS.convert(t,
TimeUnit.SECONDS));
- assertEquals(1000000 * t,
+ assertEquals(1000000L*t,
TimeUnit.NANOSECONDS.convert(t,
TimeUnit.MILLISECONDS));
- assertEquals(1000 * t,
+ assertEquals(1000L*t,
TimeUnit.NANOSECONDS.convert(t,
TimeUnit.MICROSECONDS));
assertEquals(t,
@@ -82,13 +88,12 @@ public class TimeUnitTest extends JSR166TestCase {
* nanoseconds
*/
public void testToNanos() {
- for (long t = 0; t < 10; ++t) {
- assertEquals(1000000000 * t,
+ for (long t = 0; t < 88888; ++t) {
+ assertEquals(1000000000L*t,
TimeUnit.SECONDS.toNanos(t));
-
- assertEquals(1000000 * t,
+ assertEquals(1000000L*t,
TimeUnit.MILLISECONDS.toNanos(t));
- assertEquals(1000 * t,
+ assertEquals(1000L*t,
TimeUnit.MICROSECONDS.toNanos(t));
assertEquals(t,
TimeUnit.NANOSECONDS.toNanos(t));
@@ -100,16 +105,15 @@ public class TimeUnitTest extends JSR166TestCase {
* microseconds
*/
public void testToMicros() {
- for (long t = 0; t < 10; ++t) {
- assertEquals(1000000 * t,
+ for (long t = 0; t < 88888; ++t) {
+ assertEquals(1000000L*t,
TimeUnit.SECONDS.toMicros(t));
-
- assertEquals(1000 * t,
+ assertEquals(1000L*t,
TimeUnit.MILLISECONDS.toMicros(t));
assertEquals(t,
TimeUnit.MICROSECONDS.toMicros(t));
assertEquals(t,
- TimeUnit.NANOSECONDS.toMicros(t * 1000));
+ TimeUnit.NANOSECONDS.toMicros(t*1000L));
}
}
@@ -118,16 +122,15 @@ public class TimeUnitTest extends JSR166TestCase {
* milliseconds
*/
public void testToMillis() {
- for (long t = 0; t < 10; ++t) {
- assertEquals(1000 * t,
+ for (long t = 0; t < 88888; ++t) {
+ assertEquals(1000L*t,
TimeUnit.SECONDS.toMillis(t));
-
assertEquals(t,
TimeUnit.MILLISECONDS.toMillis(t));
assertEquals(t,
- TimeUnit.MICROSECONDS.toMillis(t * 1000));
+ TimeUnit.MICROSECONDS.toMillis(t*1000L));
assertEquals(t,
- TimeUnit.NANOSECONDS.toMillis(t * 1000000));
+ TimeUnit.NANOSECONDS.toMillis(t*1000000L));
}
}
@@ -136,20 +139,18 @@ public class TimeUnitTest extends JSR166TestCase {
* seconds
*/
public void testToSeconds() {
- for (long t = 0; t < 10; ++t) {
- assertEquals(t,
+ for (long t = 0; t < 88888; ++t) {
+ assertEquals(t,
TimeUnit.SECONDS.toSeconds(t));
-
assertEquals(t,
- TimeUnit.MILLISECONDS.toSeconds(t * 1000));
+ TimeUnit.MILLISECONDS.toSeconds(t*1000L));
assertEquals(t,
- TimeUnit.MICROSECONDS.toSeconds(t * 1000000));
+ TimeUnit.MICROSECONDS.toSeconds(t*1000000L));
assertEquals(t,
- TimeUnit.NANOSECONDS.toSeconds(t * 1000000000));
+ TimeUnit.NANOSECONDS.toSeconds(t*1000000000L));
}
}
-
/**
* convert saturates positive too-large values to Long.MAX_VALUE
* and negative to LONG.MIN_VALUE
@@ -161,6 +162,7 @@ public class TimeUnitTest extends JSR166TestCase {
assertEquals(Long.MIN_VALUE,
TimeUnit.NANOSECONDS.convert(-Long.MAX_VALUE / 4,
TimeUnit.SECONDS));
+
}
/**
diff --git a/dalvik/src/main/java/dalvik/system/VMStack.java b/dalvik/src/main/java/dalvik/system/VMStack.java
index 9330c68..88d9bcd 100644
--- a/dalvik/src/main/java/dalvik/system/VMStack.java
+++ b/dalvik/src/main/java/dalvik/system/VMStack.java
@@ -40,6 +40,13 @@ public final class VMStack {
native public static ClassLoader getCallingClassLoader2();
/**
+ * Returns the class of the caller's caller's caller.
+ *
+ * @return the requested class, or {@code null}.
+ */
+ native public static Class<?> getStackClass2();
+
+ /**
* Creates an array of classes from the methods at the top of the stack.
* We continue until we reach the bottom of the stack or exceed the
* specified maximum depth. If stopAtPrivileged is set, the last