mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
8234131: Miscellaneous changes imported from jsr166 CVS 2021-01
8257671: ThreadPoolExecutor.Discard*Policy: rejected tasks are not cancelled Reviewed-by: alanb, prappo, dl
This commit is contained in:
parent
63e3bd7613
commit
270014ab4e
41 changed files with 273 additions and 207 deletions
|
@ -54,13 +54,13 @@ import java.util.List;
|
|||
* to return {@code RunnableFuture} implementations other than
|
||||
* {@code FutureTask}.
|
||||
*
|
||||
* <p><b>Extension example</b>. Here is a sketch of a class
|
||||
* <p><b>Extension example.</b> Here is a sketch of a class
|
||||
* that customizes {@link ThreadPoolExecutor} to use
|
||||
* a {@code CustomTask} class instead of the default {@code FutureTask}:
|
||||
* <pre> {@code
|
||||
* public class CustomThreadPoolExecutor extends ThreadPoolExecutor {
|
||||
*
|
||||
* static class CustomTask<V> implements RunnableFuture<V> {...}
|
||||
* static class CustomTask<V> implements RunnableFuture<V> { ... }
|
||||
*
|
||||
* protected <V> RunnableFuture<V> newTaskFor(Callable<V> c) {
|
||||
* return new CustomTask<V>(c);
|
||||
|
|
|
@ -69,7 +69,7 @@ import java.util.function.Predicate;
|
|||
* perform some action upon state updates.
|
||||
*
|
||||
* <pre> {@code
|
||||
* class Handler { void handle(); ... }
|
||||
* class Handler { void handle() { ... } }
|
||||
*
|
||||
* class X {
|
||||
* private final CopyOnWriteArraySet<Handler> handlers
|
||||
|
|
|
@ -118,7 +118,7 @@ import java.util.concurrent.locks.AbstractQueuedSynchronizer;
|
|||
* class Driver2 { // ...
|
||||
* void main() throws InterruptedException {
|
||||
* CountDownLatch doneSignal = new CountDownLatch(N);
|
||||
* Executor e = ...
|
||||
* Executor e = ...;
|
||||
*
|
||||
* for (int i = 0; i < N; ++i) // create and start threads
|
||||
* e.execute(new WorkerRunnable(doneSignal, i));
|
||||
|
@ -135,10 +135,8 @@ import java.util.concurrent.locks.AbstractQueuedSynchronizer;
|
|||
* this.i = i;
|
||||
* }
|
||||
* public void run() {
|
||||
* try {
|
||||
* doWork(i);
|
||||
* doneSignal.countDown();
|
||||
* } catch (InterruptedException ex) {} // return;
|
||||
* doWork();
|
||||
* doneSignal.countDown();
|
||||
* }
|
||||
*
|
||||
* void doWork() { ... }
|
||||
|
|
|
@ -94,7 +94,9 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||
*
|
||||
* // wait until done
|
||||
* for (Thread thread : threads)
|
||||
* thread.join();
|
||||
* try {
|
||||
* thread.join();
|
||||
* } catch (InterruptedException ex) { }
|
||||
* }
|
||||
* }}</pre>
|
||||
*
|
||||
|
|
|
@ -57,8 +57,8 @@ import java.util.concurrent.locks.LockSupport;
|
|||
* <pre> {@code
|
||||
* class FillAndEmpty {
|
||||
* Exchanger<DataBuffer> exchanger = new Exchanger<>();
|
||||
* DataBuffer initialEmptyBuffer = ... a made-up type
|
||||
* DataBuffer initialFullBuffer = ...
|
||||
* DataBuffer initialEmptyBuffer = ...; // a made-up type
|
||||
* DataBuffer initialFullBuffer = ...;
|
||||
*
|
||||
* class FillingLoop implements Runnable {
|
||||
* public void run() {
|
||||
|
@ -69,7 +69,7 @@ import java.util.concurrent.locks.LockSupport;
|
|||
* if (currentBuffer.isFull())
|
||||
* currentBuffer = exchanger.exchange(currentBuffer);
|
||||
* }
|
||||
* } catch (InterruptedException ex) { ... handle ... }
|
||||
* } catch (InterruptedException ex) { ... handle ...}
|
||||
* }
|
||||
* }
|
||||
*
|
||||
|
|
|
@ -117,7 +117,7 @@ import java.util.List;
|
|||
* if (!pool.awaitTermination(60, TimeUnit.SECONDS))
|
||||
* System.err.println("Pool did not terminate");
|
||||
* }
|
||||
* } catch (InterruptedException ie) {
|
||||
* } catch (InterruptedException ex) {
|
||||
* // (Re-)Cancel if current thread also interrupted
|
||||
* pool.shutdownNow();
|
||||
* // Preserve interrupt status
|
||||
|
|
|
@ -56,8 +56,8 @@ package java.util.concurrent;
|
|||
* <pre> {@code
|
||||
* interface ArchiveSearcher { String search(String target); }
|
||||
* class App {
|
||||
* ExecutorService executor = ...
|
||||
* ArchiveSearcher searcher = ...
|
||||
* ExecutorService executor = ...;
|
||||
* ArchiveSearcher searcher = ...;
|
||||
* void showSearch(String target) throws InterruptedException {
|
||||
* Callable<String> task = () -> searcher.search(target);
|
||||
* Future<String> future = executor.submit(task);
|
||||
|
@ -88,25 +88,28 @@ package java.util.concurrent;
|
|||
public interface Future<V> {
|
||||
|
||||
/**
|
||||
* Attempts to cancel execution of this task. This attempt will
|
||||
* 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 {@code cancel} is called,
|
||||
* this task should never run. If the task has already started,
|
||||
* then the {@code mayInterruptIfRunning} parameter determines
|
||||
* whether the thread executing this task should be interrupted in
|
||||
* an attempt to stop the task.
|
||||
* Attempts to cancel execution of this task. This method has no
|
||||
* effect if the task is already completed or cancelled, or could
|
||||
* not be cancelled for some other reason. Otherwise, if this
|
||||
* task has not started when {@code cancel} is called, this task
|
||||
* should never run. If the task has already started, then the
|
||||
* {@code mayInterruptIfRunning} parameter determines whether the
|
||||
* thread executing this task (when known by the implementation)
|
||||
* is interrupted in an attempt to stop the task.
|
||||
*
|
||||
* <p>After this method returns, subsequent calls to {@link #isDone} will
|
||||
* always return {@code true}. Subsequent calls to {@link #isCancelled}
|
||||
* will always return {@code true} if this method returned {@code true}.
|
||||
* <p>The return value from this method does not necessarily
|
||||
* indicate whether the task is now cancelled; use {@link
|
||||
* #isCancelled}.
|
||||
*
|
||||
* @param mayInterruptIfRunning {@code true} if the thread executing this
|
||||
* task should be interrupted; otherwise, in-progress tasks are allowed
|
||||
* to complete
|
||||
* @param mayInterruptIfRunning {@code true} if the thread
|
||||
* executing this task should be interrupted (if the thread is
|
||||
* known to the implementation); otherwise, in-progress tasks are
|
||||
* allowed to complete
|
||||
* @return {@code false} if the task could not be cancelled,
|
||||
* typically because it has already completed normally;
|
||||
* {@code true} otherwise
|
||||
* typically because it has already completed; {@code true}
|
||||
* otherwise. If two or more threads cause a task to be cancelled,
|
||||
* then at least one of them returns {@code true}. Implementations
|
||||
* may provide stronger guarantees.
|
||||
*/
|
||||
boolean cancel(boolean mayInterruptIfRunning);
|
||||
|
||||
|
|
|
@ -245,7 +245,7 @@ import java.util.concurrent.locks.LockSupport;
|
|||
* be appropriate for extremely small per-phase task bodies (thus
|
||||
* high rates), or up to hundreds for extremely large ones.
|
||||
*
|
||||
* <p><b>Implementation notes</b>: This implementation restricts the
|
||||
* <p><b>Implementation notes:</b> This implementation restricts the
|
||||
* maximum number of parties to 65535. Attempts to register additional
|
||||
* parties result in {@code IllegalStateException}. However, you can and
|
||||
* should create tiered phasers to accommodate arbitrarily large sets
|
||||
|
@ -919,7 +919,7 @@ public class Phaser {
|
|||
* <pre> {@code
|
||||
* Phaser phaser = new Phaser() {
|
||||
* protected boolean onAdvance(int phase, int parties) { return false; }
|
||||
* }}</pre>
|
||||
* };}</pre>
|
||||
*
|
||||
* @param phase the current phase number on entry to this method,
|
||||
* before this phaser is advanced
|
||||
|
|
|
@ -87,7 +87,7 @@ import jdk.internal.util.ArraysSupport;
|
|||
* <pre> {@code
|
||||
* class FIFOEntry<E extends Comparable<? super E>>
|
||||
* implements Comparable<FIFOEntry<E>> {
|
||||
* static final AtomicLong seq = new AtomicLong(0);
|
||||
* static final AtomicLong seq = new AtomicLong();
|
||||
* final long seqNum;
|
||||
* final E entry;
|
||||
* public FIFOEntry(E entry) {
|
||||
|
@ -290,7 +290,9 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E>
|
|||
if (allocationSpinLock == 0 &&
|
||||
ALLOCATIONSPINLOCK.compareAndSet(this, 0, 1)) {
|
||||
try {
|
||||
int growth = oldCap < 64 ? oldCap + 2 : oldCap >> 1;
|
||||
int growth = (oldCap < 64)
|
||||
? (oldCap + 2) // grow faster if small
|
||||
: (oldCap >> 1);
|
||||
int newCap = ArraysSupport.newLength(oldCap, 1, growth);
|
||||
if (queue == array)
|
||||
newArray = new Object[newCap];
|
||||
|
|
|
@ -66,7 +66,7 @@ import java.util.concurrent.locks.AbstractQueuedSynchronizer;
|
|||
*
|
||||
* // Not a particularly efficient data structure; just for demo
|
||||
*
|
||||
* protected Object[] items = ... whatever kinds of items being managed
|
||||
* protected Object[] items = ...; // whatever kinds of items being managed
|
||||
* protected boolean[] used = new boolean[MAX_AVAILABLE];
|
||||
*
|
||||
* protected synchronized Object getNextAvailableItem() {
|
||||
|
|
|
@ -41,6 +41,7 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Consumer;
|
||||
|
@ -175,11 +176,11 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
/*
|
||||
* Most mechanics are handled by BufferedSubscription. This class
|
||||
* mainly tracks subscribers and ensures sequentiality, by using
|
||||
* built-in synchronization locks across public methods. Using
|
||||
* built-in locks works well in the most typical case in which
|
||||
* only one thread submits items. We extend this idea in
|
||||
* submission methods by detecting single-ownership to reduce
|
||||
* producer-consumer synchronization strength.
|
||||
* locks across public methods, to ensure thread-safety in the
|
||||
* presence of multiple sources and maintain acquire-release
|
||||
* ordering around user operations. However, we also track whether
|
||||
* there is only a single source, and if so streamline some buffer
|
||||
* operations by avoiding some atomics.
|
||||
*/
|
||||
|
||||
/** The largest possible power of two array size. */
|
||||
|
@ -234,6 +235,8 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
*/
|
||||
BufferedSubscription<T> clients;
|
||||
|
||||
/** Lock for exclusion across multiple sources */
|
||||
final ReentrantLock lock;
|
||||
/** Run status, updated only within locks */
|
||||
volatile boolean closed;
|
||||
/** Set true on first call to subscribe, to initialize possible owner */
|
||||
|
@ -274,6 +277,7 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
throw new NullPointerException();
|
||||
if (maxBufferCapacity <= 0)
|
||||
throw new IllegalArgumentException("capacity must be positive");
|
||||
this.lock = new ReentrantLock();
|
||||
this.executor = executor;
|
||||
this.onNextHandler = handler;
|
||||
this.maxBufferCapacity = roundCapacity(maxBufferCapacity);
|
||||
|
@ -337,13 +341,15 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
*/
|
||||
public void subscribe(Subscriber<? super T> subscriber) {
|
||||
if (subscriber == null) throw new NullPointerException();
|
||||
ReentrantLock lock = this.lock;
|
||||
int max = maxBufferCapacity; // allocate initial array
|
||||
Object[] array = new Object[max < INITIAL_CAPACITY ?
|
||||
max : INITIAL_CAPACITY];
|
||||
BufferedSubscription<T> subscription =
|
||||
new BufferedSubscription<T>(subscriber, executor, onNextHandler,
|
||||
array, max);
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (!subscribed) {
|
||||
subscribed = true;
|
||||
owner = Thread.currentThread();
|
||||
|
@ -378,6 +384,8 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
pred = b;
|
||||
b = next;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -390,7 +398,9 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
if (item == null) throw new NullPointerException();
|
||||
int lag = 0;
|
||||
boolean complete, unowned;
|
||||
synchronized (this) {
|
||||
ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
Thread t = Thread.currentThread(), o;
|
||||
BufferedSubscription<T> b = clients;
|
||||
if ((unowned = ((o = owner) != t)) && o != null)
|
||||
|
@ -421,6 +431,8 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
if (retries != null || cleanMe)
|
||||
lag = retryOffer(item, nanos, onDrop, retries, lag, cleanMe);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
if (complete)
|
||||
throw new IllegalStateException("Closed");
|
||||
|
@ -609,14 +621,18 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
* subscribers have yet completed.
|
||||
*/
|
||||
public void close() {
|
||||
ReentrantLock lock = this.lock;
|
||||
if (!closed) {
|
||||
BufferedSubscription<T> b;
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
// no need to re-check closed here
|
||||
b = clients;
|
||||
clients = null;
|
||||
owner = null;
|
||||
closed = true;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
while (b != null) {
|
||||
BufferedSubscription<T> next = b.next;
|
||||
|
@ -641,9 +657,11 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
public void closeExceptionally(Throwable error) {
|
||||
if (error == null)
|
||||
throw new NullPointerException();
|
||||
ReentrantLock lock = this.lock;
|
||||
if (!closed) {
|
||||
BufferedSubscription<T> b;
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
b = clients;
|
||||
if (!closed) { // don't clobber racing close
|
||||
closedException = error;
|
||||
|
@ -651,6 +669,8 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
owner = null;
|
||||
closed = true;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
while (b != null) {
|
||||
BufferedSubscription<T> next = b.next;
|
||||
|
@ -688,7 +708,9 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
*/
|
||||
public boolean hasSubscribers() {
|
||||
boolean nonEmpty = false;
|
||||
synchronized (this) {
|
||||
ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
for (BufferedSubscription<T> b = clients; b != null;) {
|
||||
BufferedSubscription<T> next = b.next;
|
||||
if (b.isClosed()) {
|
||||
|
@ -700,6 +722,8 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return nonEmpty;
|
||||
}
|
||||
|
@ -710,9 +734,15 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
* @return the number of current subscribers
|
||||
*/
|
||||
public int getNumberOfSubscribers() {
|
||||
synchronized (this) {
|
||||
return cleanAndCount();
|
||||
int n;
|
||||
ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
n = cleanAndCount();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -742,7 +772,9 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
*/
|
||||
public List<Subscriber<? super T>> getSubscribers() {
|
||||
ArrayList<Subscriber<? super T>> subs = new ArrayList<>();
|
||||
synchronized (this) {
|
||||
ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
BufferedSubscription<T> pred = null, next;
|
||||
for (BufferedSubscription<T> b = clients; b != null; b = next) {
|
||||
next = b.next;
|
||||
|
@ -758,6 +790,8 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
pred = b;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return subs;
|
||||
}
|
||||
|
@ -771,8 +805,11 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
*/
|
||||
public boolean isSubscribed(Subscriber<? super T> subscriber) {
|
||||
if (subscriber == null) throw new NullPointerException();
|
||||
boolean subscribed = false;
|
||||
ReentrantLock lock = this.lock;
|
||||
if (!closed) {
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
BufferedSubscription<T> pred = null, next;
|
||||
for (BufferedSubscription<T> b = clients; b != null; b = next) {
|
||||
next = b.next;
|
||||
|
@ -783,14 +820,16 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
else
|
||||
pred.next = next;
|
||||
}
|
||||
else if (subscriber.equals(b.subscriber))
|
||||
return true;
|
||||
else if (subscribed = subscriber.equals(b.subscriber))
|
||||
break;
|
||||
else
|
||||
pred = b;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return subscribed;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -803,7 +842,9 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
public long estimateMinimumDemand() {
|
||||
long min = Long.MAX_VALUE;
|
||||
boolean nonEmpty = false;
|
||||
synchronized (this) {
|
||||
ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
BufferedSubscription<T> pred = null, next;
|
||||
for (BufferedSubscription<T> b = clients; b != null; b = next) {
|
||||
int n; long d;
|
||||
|
@ -822,6 +863,8 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
pred = b;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return nonEmpty ? min : 0;
|
||||
}
|
||||
|
@ -834,7 +877,9 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
*/
|
||||
public int estimateMaximumLag() {
|
||||
int max = 0;
|
||||
synchronized (this) {
|
||||
ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
BufferedSubscription<T> pred = null, next;
|
||||
for (BufferedSubscription<T> b = clients; b != null; b = next) {
|
||||
int n;
|
||||
|
@ -852,6 +897,8 @@ public class SubmissionPublisher<T> implements Publisher<T>,
|
|||
pred = b;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
|
|
@ -221,13 +221,18 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||
* simple feedback control mechanism that will slow down the rate that
|
||||
* new tasks are submitted.
|
||||
*
|
||||
* <li>In {@link ThreadPoolExecutor.DiscardPolicy}, a task that
|
||||
* cannot be executed is simply dropped.
|
||||
* <li>In {@link ThreadPoolExecutor.DiscardPolicy}, a task that cannot
|
||||
* be executed is simply dropped. This policy is designed only for
|
||||
* those rare cases in which task completion is never relied upon.
|
||||
*
|
||||
* <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.)
|
||||
* causing this to be repeated.) This policy is rarely acceptable. In
|
||||
* nearly all cases, you should also cancel the task to cause an
|
||||
* exception in any component waiting for its completion, and/or log
|
||||
* the failure, as illustrated in {@link
|
||||
* ThreadPoolExecutor.DiscardOldestPolicy} documentation.
|
||||
*
|
||||
* </ol>
|
||||
*
|
||||
|
@ -272,7 +277,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||
*
|
||||
* </dl>
|
||||
*
|
||||
* <p><b>Extension example</b>. Most extensions of this class
|
||||
* <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:
|
||||
*
|
||||
|
@ -1149,8 +1154,10 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
|
|||
|
||||
/**
|
||||
* Creates a new {@code ThreadPoolExecutor} with the given initial
|
||||
* parameters, the default thread factory and the default rejected
|
||||
* execution handler.
|
||||
* parameters, the
|
||||
* {@linkplain Executors#defaultThreadFactory default thread factory}
|
||||
* and the {@linkplain ThreadPoolExecutor.AbortPolicy
|
||||
* default rejected execution handler}.
|
||||
*
|
||||
* <p>It may be more convenient to use one of the {@link Executors}
|
||||
* factory methods instead of this general purpose constructor.
|
||||
|
@ -1184,7 +1191,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
|
|||
|
||||
/**
|
||||
* Creates a new {@code ThreadPoolExecutor} with the given initial
|
||||
* parameters and {@linkplain ThreadPoolExecutor.AbortPolicy
|
||||
* parameters and the {@linkplain ThreadPoolExecutor.AbortPolicy
|
||||
* default rejected execution handler}.
|
||||
*
|
||||
* @param corePoolSize the number of threads to keep in the pool, even
|
||||
|
@ -1220,7 +1227,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
|
|||
|
||||
/**
|
||||
* Creates a new {@code ThreadPoolExecutor} with the given initial
|
||||
* parameters and
|
||||
* parameters and the
|
||||
* {@linkplain Executors#defaultThreadFactory default thread factory}.
|
||||
*
|
||||
* @param corePoolSize the number of threads to keep in the pool, even
|
||||
|
@ -2081,7 +2088,20 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
|
|||
/**
|
||||
* A handler for rejected tasks that discards the oldest unhandled
|
||||
* request and then retries {@code execute}, unless the executor
|
||||
* is shut down, in which case the task is discarded.
|
||||
* is shut down, in which case the task is discarded. This policy is
|
||||
* rarely useful in cases where other threads may be waiting for
|
||||
* tasks to terminate, or failures must be recorded. Instead consider
|
||||
* using a handler of the form:
|
||||
* <pre> {@code
|
||||
* new RejectedExecutionHandler() {
|
||||
* public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
|
||||
* Runnable dropped = e.getQueue().poll();
|
||||
* if (dropped instanceof Future<?>) {
|
||||
* ((Future<?>)dropped).cancel(false);
|
||||
* // also consider logging the failure
|
||||
* }
|
||||
* e.execute(r); // retry
|
||||
* }}}</pre>
|
||||
*/
|
||||
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
|
||||
/**
|
||||
|
|
|
@ -68,9 +68,8 @@ public class AtomicBoolean implements java.io.Serializable {
|
|||
* @param initialValue the initial value
|
||||
*/
|
||||
public AtomicBoolean(boolean initialValue) {
|
||||
if (initialValue) {
|
||||
if (initialValue)
|
||||
value = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -62,7 +62,7 @@ import java.lang.invoke.VarHandle;
|
|||
*
|
||||
* private static final AtomicReferenceFieldUpdater<Node, Node> leftUpdater =
|
||||
* AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "left");
|
||||
* private static AtomicReferenceFieldUpdater<Node, Node> rightUpdater =
|
||||
* private static final AtomicReferenceFieldUpdater<Node, Node> rightUpdater =
|
||||
* AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "right");
|
||||
*
|
||||
* Node getLeft() { return left; }
|
||||
|
|
|
@ -111,16 +111,17 @@ public class DoubleAccumulator extends Striped64 implements Serializable {
|
|||
|| ((r = doubleToRawLongBits
|
||||
(function.applyAsDouble(longBitsToDouble(b = base), x))) != b
|
||||
&& !casBase(b, r))) {
|
||||
int index = getProbe();
|
||||
boolean uncontended = true;
|
||||
if (cs == null
|
||||
|| (m = cs.length - 1) < 0
|
||||
|| (c = cs[getProbe() & m]) == null
|
||||
|| (c = cs[index & m]) == null
|
||||
|| !(uncontended =
|
||||
((r = doubleToRawLongBits
|
||||
(function.applyAsDouble
|
||||
(longBitsToDouble(v = c.value), x))) == v)
|
||||
|| c.cas(v, r)))
|
||||
doubleAccumulate(x, function, uncontended);
|
||||
doubleAccumulate(x, function, uncontended, index);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -92,13 +92,14 @@ public class DoubleAdder extends Striped64 implements Serializable {
|
|||
!casBase(b = base,
|
||||
Double.doubleToRawLongBits
|
||||
(Double.longBitsToDouble(b) + x))) {
|
||||
int index = getProbe();
|
||||
boolean uncontended = true;
|
||||
if (cs == null || (m = cs.length - 1) < 0 ||
|
||||
(c = cs[getProbe() & m]) == null ||
|
||||
(c = cs[index & m]) == null ||
|
||||
!(uncontended = c.cas(v = c.value,
|
||||
Double.doubleToRawLongBits
|
||||
(Double.longBitsToDouble(v) + x))))
|
||||
doubleAccumulate(x, null, uncontended);
|
||||
doubleAccumulate(x, null, uncontended, index);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -108,14 +108,15 @@ public class LongAccumulator extends Striped64 implements Serializable {
|
|||
if ((cs = cells) != null
|
||||
|| ((r = function.applyAsLong(b = base, x)) != b
|
||||
&& !casBase(b, r))) {
|
||||
int index = getProbe();
|
||||
boolean uncontended = true;
|
||||
if (cs == null
|
||||
|| (m = cs.length - 1) < 0
|
||||
|| (c = cs[getProbe() & m]) == null
|
||||
|| (c = cs[index & m]) == null
|
||||
|| !(uncontended =
|
||||
(r = function.applyAsLong(v = c.value, x)) == v
|
||||
|| c.cas(v, r)))
|
||||
longAccumulate(x, function, uncontended);
|
||||
longAccumulate(x, function, uncontended, index);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,11 +85,12 @@ public class LongAdder extends Striped64 implements Serializable {
|
|||
public void add(long x) {
|
||||
Cell[] cs; long b, v; int m; Cell c;
|
||||
if ((cs = cells) != null || !casBase(b = base, b + x)) {
|
||||
int index = getProbe();
|
||||
boolean uncontended = true;
|
||||
if (cs == null || (m = cs.length - 1) < 0 ||
|
||||
(c = cs[getProbe() & m]) == null ||
|
||||
(c = cs[index & m]) == null ||
|
||||
!(uncontended = c.cas(v = c.value, v + x)))
|
||||
longAccumulate(x, null, uncontended);
|
||||
longAccumulate(x, null, uncontended, index);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ abstract class Striped64 extends Number {
|
|||
volatile long value;
|
||||
Cell(long x) { value = x; }
|
||||
final boolean cas(long cmp, long val) {
|
||||
return VALUE.compareAndSet(this, cmp, val);
|
||||
return VALUE.weakCompareAndSetRelease(this, cmp, val);
|
||||
}
|
||||
final void reset() {
|
||||
VALUE.setVolatile(this, 0L);
|
||||
|
@ -178,7 +178,7 @@ abstract class Striped64 extends Number {
|
|||
* CASes the base field.
|
||||
*/
|
||||
final boolean casBase(long cmp, long val) {
|
||||
return BASE.compareAndSet(this, cmp, val);
|
||||
return BASE.weakCompareAndSetRelease(this, cmp, val);
|
||||
}
|
||||
|
||||
final long getAndSetBase(long val) {
|
||||
|
@ -224,20 +224,19 @@ abstract class Striped64 extends Number {
|
|||
* @param fn the update function, or null for add (this convention
|
||||
* avoids the need for an extra field or function in LongAdder).
|
||||
* @param wasUncontended false if CAS failed before call
|
||||
* @param index thread index from getProbe
|
||||
*/
|
||||
final void longAccumulate(long x, LongBinaryOperator fn,
|
||||
boolean wasUncontended) {
|
||||
int h;
|
||||
if ((h = getProbe()) == 0) {
|
||||
boolean wasUncontended, int index) {
|
||||
if (index == 0) {
|
||||
ThreadLocalRandom.current(); // force initialization
|
||||
h = getProbe();
|
||||
index = getProbe();
|
||||
wasUncontended = true;
|
||||
}
|
||||
boolean collide = false; // True if last slot nonempty
|
||||
done: for (;;) {
|
||||
for (boolean collide = false;;) { // True if last slot nonempty
|
||||
Cell[] cs; Cell c; int n; long v;
|
||||
if ((cs = cells) != null && (n = cs.length) > 0) {
|
||||
if ((c = cs[(n - 1) & h]) == null) {
|
||||
if ((c = cs[(n - 1) & index]) == null) {
|
||||
if (cellsBusy == 0) { // Try to attach new Cell
|
||||
Cell r = new Cell(x); // Optimistically create
|
||||
if (cellsBusy == 0 && casCellsBusy()) {
|
||||
|
@ -245,9 +244,9 @@ abstract class Striped64 extends Number {
|
|||
Cell[] rs; int m, j;
|
||||
if ((rs = cells) != null &&
|
||||
(m = rs.length) > 0 &&
|
||||
rs[j = (m - 1) & h] == null) {
|
||||
rs[j = (m - 1) & index] == null) {
|
||||
rs[j] = r;
|
||||
break done;
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
cellsBusy = 0;
|
||||
|
@ -276,15 +275,15 @@ abstract class Striped64 extends Number {
|
|||
collide = false;
|
||||
continue; // Retry with expanded table
|
||||
}
|
||||
h = advanceProbe(h);
|
||||
index = advanceProbe(index);
|
||||
}
|
||||
else if (cellsBusy == 0 && cells == cs && casCellsBusy()) {
|
||||
try { // Initialize table
|
||||
if (cells == cs) {
|
||||
Cell[] rs = new Cell[2];
|
||||
rs[h & 1] = new Cell(x);
|
||||
rs[index & 1] = new Cell(x);
|
||||
cells = rs;
|
||||
break done;
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
cellsBusy = 0;
|
||||
|
@ -293,7 +292,7 @@ abstract class Striped64 extends Number {
|
|||
// Fall back on using base
|
||||
else if (casBase(v = base,
|
||||
(fn == null) ? v + x : fn.applyAsLong(v, x)))
|
||||
break done;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,18 +309,16 @@ abstract class Striped64 extends Number {
|
|||
* maintained by copy/paste/adapt.
|
||||
*/
|
||||
final void doubleAccumulate(double x, DoubleBinaryOperator fn,
|
||||
boolean wasUncontended) {
|
||||
int h;
|
||||
if ((h = getProbe()) == 0) {
|
||||
boolean wasUncontended, int index) {
|
||||
if (index == 0) {
|
||||
ThreadLocalRandom.current(); // force initialization
|
||||
h = getProbe();
|
||||
index = getProbe();
|
||||
wasUncontended = true;
|
||||
}
|
||||
boolean collide = false; // True if last slot nonempty
|
||||
done: for (;;) {
|
||||
for (boolean collide = false;;) { // True if last slot nonempty
|
||||
Cell[] cs; Cell c; int n; long v;
|
||||
if ((cs = cells) != null && (n = cs.length) > 0) {
|
||||
if ((c = cs[(n - 1) & h]) == null) {
|
||||
if ((c = cs[(n - 1) & index]) == null) {
|
||||
if (cellsBusy == 0) { // Try to attach new Cell
|
||||
Cell r = new Cell(Double.doubleToRawLongBits(x));
|
||||
if (cellsBusy == 0 && casCellsBusy()) {
|
||||
|
@ -329,9 +326,9 @@ abstract class Striped64 extends Number {
|
|||
Cell[] rs; int m, j;
|
||||
if ((rs = cells) != null &&
|
||||
(m = rs.length) > 0 &&
|
||||
rs[j = (m - 1) & h] == null) {
|
||||
rs[j = (m - 1) & index] == null) {
|
||||
rs[j] = r;
|
||||
break done;
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
cellsBusy = 0;
|
||||
|
@ -359,15 +356,15 @@ abstract class Striped64 extends Number {
|
|||
collide = false;
|
||||
continue; // Retry with expanded table
|
||||
}
|
||||
h = advanceProbe(h);
|
||||
index = advanceProbe(index);
|
||||
}
|
||||
else if (cellsBusy == 0 && cells == cs && casCellsBusy()) {
|
||||
try { // Initialize table
|
||||
if (cells == cs) {
|
||||
Cell[] rs = new Cell[2];
|
||||
rs[h & 1] = new Cell(Double.doubleToRawLongBits(x));
|
||||
rs[index & 1] = new Cell(Double.doubleToRawLongBits(x));
|
||||
cells = rs;
|
||||
break done;
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
cellsBusy = 0;
|
||||
|
@ -375,7 +372,7 @@ abstract class Striped64 extends Number {
|
|||
}
|
||||
// Fall back on using base
|
||||
else if (casBase(v = base, apply(fn, v, x)))
|
||||
break done;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
* <pre> {@code
|
||||
* class Sequencer {
|
||||
* private final AtomicLong sequenceNumber
|
||||
* = new AtomicLong(0);
|
||||
* = new AtomicLong(17);
|
||||
* public long next() {
|
||||
* return sequenceNumber.getAndIncrement();
|
||||
* }
|
||||
|
|
|
@ -137,13 +137,13 @@ import jdk.internal.misc.Unsafe;
|
|||
* of exclusive synchronization takes the form:
|
||||
*
|
||||
* <pre>
|
||||
* Acquire:
|
||||
* <em>Acquire:</em>
|
||||
* while (!tryAcquire(arg)) {
|
||||
* <em>enqueue thread if it is not already queued</em>;
|
||||
* <em>possibly block current thread</em>;
|
||||
* }
|
||||
*
|
||||
* Release:
|
||||
* <em>Release:</em>
|
||||
* if (tryRelease(arg))
|
||||
* <em>unblock the first queued thread</em>;
|
||||
* </pre>
|
||||
|
|
|
@ -550,7 +550,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
|
|||
*
|
||||
* <pre> {@code
|
||||
* class X {
|
||||
* ReentrantLock lock = new ReentrantLock();
|
||||
* final ReentrantLock lock = new ReentrantLock();
|
||||
* // ...
|
||||
* public void m() {
|
||||
* assert lock.getHoldCount() == 0;
|
||||
|
@ -580,7 +580,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
|
|||
*
|
||||
* <pre> {@code
|
||||
* class X {
|
||||
* ReentrantLock lock = new ReentrantLock();
|
||||
* final ReentrantLock lock = new ReentrantLock();
|
||||
* // ...
|
||||
*
|
||||
* public void m() {
|
||||
|
@ -594,7 +594,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
|
|||
*
|
||||
* <pre> {@code
|
||||
* class X {
|
||||
* ReentrantLock lock = new ReentrantLock();
|
||||
* final ReentrantLock lock = new ReentrantLock();
|
||||
* // ...
|
||||
*
|
||||
* public void m() {
|
||||
|
|
|
@ -128,7 +128,7 @@ import jdk.internal.vm.annotation.ReservedStackAccess;
|
|||
* 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 perform
|
||||
* <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):
|
||||
|
@ -149,7 +149,7 @@ import jdk.internal.vm.annotation.ReservedStackAccess;
|
|||
* // Recheck state because another thread might have
|
||||
* // acquired write lock and changed state before we did.
|
||||
* if (!cacheValid) {
|
||||
* data = ...
|
||||
* data = ...;
|
||||
* cacheValid = true;
|
||||
* }
|
||||
* // Downgrade by acquiring read lock before releasing write lock
|
||||
|
|
|
@ -212,8 +212,8 @@ import jdk.internal.vm.annotation.ReservedStackAccess;
|
|||
* }
|
||||
* }
|
||||
*
|
||||
* // Upgrade read lock to write lock
|
||||
* void moveIfAtOrigin(double newX, double newY) {
|
||||
* // upgrade read lock to write lock
|
||||
* void moveIfAtOrigin2(double newX, double newY) {
|
||||
* long stamp = sl.readLock();
|
||||
* try {
|
||||
* while (x == 0.0 && y == 0.0) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue