Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Licensed 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 com.stratio.cassandra.lucene.util;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* This class is a specialized extension of the ThreadPoolExecutor class.
*
* Two functionalities had been added to this subclass. 1) The execute method of the ThreadPoolExecutor will block in
* case the queue is full and only unblock when the queue is dequeued - that is a task that is currently in the queue is
* removed and handled by the ThreadPoolExecutor. 2) Client code can await for the event of all tasks being run to
* conclusion. Client code which actively chose to wait for this occurrence should call await on the instance of his
* ThreadPoolExecutor. This differs from awaitTermination as it does not require any call to shutdown.
*
* This subclass of ThreadPoolExecutor also takes away the max threads capabilities of the ThreadPoolExecutor superclass
* and internally sets the amount of maximum threads to be the size of the core threads. This is done since threads over
* the core size and under the max are instantiated only once the queue is full, but the
* NotifyingBlockingThreadPoolExecutor will block once the queue is full.
*
* @author Yaneeve Shekel {@literal &} Amir Kirsh
*/
public class NotifyingBlockingThreadPoolExecutor extends ThreadPoolExecutor {
/**
* Counts the number of current tasks in process.
*/
private AtomicInteger tasksInProcess = new AtomicInteger();
/**
* This is the Synchronizer instance that is used in order to notify all interested code of when all the tasks that
* have been submitted to the execute() method have run to conclusion. This notification can occur a numerous amount
* of times. It is all up to the client code. Whenever the ThreadPoolExecutor concludes to run all the tasks the
* Synchronizer object will be notified and will in turn notify the code which is waiting on it.
*/
private Synchronizer synchronizer = new Synchronizer();
/**
* This constructor is used in order to maintain the first functionality specified above. It does so by using an
* ArrayBlockingQueue and the BlockThenRunPolicy that is defined in this class. This constructor allows to give a
* timeout for the wait on new task insertion and to react upon such a timeout if occurs.
*
* @param poolSize is the amount of threads that this pool may have alive at any given time
* @param queueSize is the size of the queue. This number should be at least as the pool size to make
* sense (otherwise there are unused threads), thus if the number sent is smaller, the
* poolSize is used for the size of the queue. Recommended value is twice the poolSize.
* @param keepAliveTime is the amount of time after which an inactive thread is terminated
* @param keepAliveTimeUnit is the unit of time to use with the previous parameter
* @param maxBlockingTime is the maximum time to wait on the queue of tasks before calling the BlockingTimeout
* callback
* @param maxBlockingTimeUnit is the unit of time to use with the previous parameter
* @param blockingTimeCallback is the callback method to call when a timeout occurs while blocking on getting a new
* task, the return value of this Callable is Boolean, indicating whether to keep
* blocking (true) or stop (false). In case false is returned from the
* blockingTimeCallback, this executer will throw a RejectedExecutionException
*/
public NotifyingBlockingThreadPoolExecutor(int poolSize,
int queueSize,
long keepAliveTime,
TimeUnit keepAliveTimeUnit,
long maxBlockingTime,
TimeUnit maxBlockingTimeUnit,
Callable blockingTimeCallback) {
super(poolSize, // Core size
poolSize, // Max size
keepAliveTime, keepAliveTimeUnit,
// not smaller than the poolSize (to avoid redundant threads)
new ArrayBlockingQueue(Math.max(poolSize, queueSize), true),
// When super invokes the reject method this class will ensure a blocking try
new BlockThenRunPolicy(maxBlockingTime, maxBlockingTimeUnit, blockingTimeCallback));
super.allowCoreThreadTimeOut(true); // Time out the core threads
}
/**
* This constructor is used in order to maintain the first functionality specified above. It does so by using an
* ArrayBlockingQueue and the BlockThenRunPolicy that is defined in this class. Using this constructor, waiting time
* on new task insertion is unlimited.
*
* @param poolSize is the amount of threads that this pool may have alive at any given time.
* @param queueSize is the size of the queue. This number should be at least as the pool size to make sense
* (otherwise there are unused threads), thus if the number sent is smaller, the poolSize is
* used for the size of the queue. Recommended value is twice the poolSize.
* @param keepAliveTime is the amount of time after which an inactive thread is terminated.
* @param unit is the unit of time to use with the previous parameter.
*/
public NotifyingBlockingThreadPoolExecutor(int poolSize, int queueSize, long keepAliveTime, TimeUnit unit) {
super(poolSize, // Core size
poolSize, // Max size
keepAliveTime, unit,
// not smaller than the poolSize (to avoid redundant threads)
new ArrayBlockingQueue(Math.max(poolSize, queueSize), true),
// When super invokes the reject method this class will ensure a blocking try.
new BlockThenRunPolicy());
super.allowCoreThreadTimeOut(true); // Time out the core threads.
}
/**
* Before calling super's version of this method, the amount of tasks which are currently in process is first
* incremented.
*
* @see ThreadPoolExecutor#execute(Runnable)
*/
@Override
public void execute(Runnable task) {
// count a new task in process
tasksInProcess.incrementAndGet();
try {
super.execute(task);
} catch (RuntimeException | Error e) { // specifically handle RejectedExecutionException
tasksInProcess.decrementAndGet();
throw e;
}
}
/**
* After calling super's implementation of this method, the amount of tasks which are currently in process is
* decremented. Finally, if the amount of tasks currently running is zero the synchronizer's signallAll() method is
* invoked, thus anyone awaiting on this instance of ThreadPoolExecutor is released.
*
* @see ThreadPoolExecutor#afterExecute(Runnable, Throwable)
*/
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
// synchronizing on the pool (and actually all its threads)
// the synchronization is needed to avoid more than one signal if two or more
// threads decrement almost together and come to the if with 0 tasks together
synchronized (this) {
tasksInProcess.decrementAndGet();
if (tasksInProcess.intValue() == 0) {
synchronizer.signalAll();
}
}
}
/**
* Internally calls on super's setCorePoolSize and setMaximumPoolSize methods with the given method argument.
*
* @see ThreadPoolExecutor#setCorePoolSize(int)
*/
@Override
public void setCorePoolSize(int corePoolSize) {
super.setCorePoolSize(corePoolSize);
super.setMaximumPoolSize(corePoolSize);
}
/**
* Does Nothing!
*
* @throws UnsupportedOperationException in any event
* @see ThreadPoolExecutor#setMaximumPoolSize(int)
*/
@Override
public void setMaximumPoolSize(int maximumPoolSize) {
throw new UnsupportedOperationException("setMaximumPoolSize is not supported.");
}
/**
* Does Nothing! MUST NOT CHANGE OUR BUILT IN RejectedExecutionHandler
*
* @throws UnsupportedOperationException in any event
* @see ThreadPoolExecutor#setRejectedExecutionHandler(RejectedExecutionHandler)
*/
@Override
public void setRejectedExecutionHandler(RejectedExecutionHandler handler) {
throw new UnsupportedOperationException("setRejectedExecutionHandler is not allowed on this class.");
}
/**
* A blocking wait for this ThreadPool to be in idle state, which means that there are no more tasks in the Queue or
* currently executed by one of the threads. BE AWARE that this method may get out from blocking state when a task
* is currently sent to the ThreadPool not from this thread context. Thus it is not safe to call this method in case
* there are several threads feeding the TreadPool with tasks (calling execute). The safe way to call this method is
* from the thread that is calling execute and when there is only one such thread. Note that this method differs
* from awaitTemination, as it can be called without shutting down the ThreadPoolExecuter.
*
* @throws InterruptedException when the internal condition throws it.
*/
public void await() throws InterruptedException {
synchronizer.await();
}
/**
* A blocking wait for this ThreadPool to be in idle state or a certain timeout to elapse. Works the same as the
* await() method, except for adding the timeout condition.
*
* @param timeout The timeout.
* @param timeUnit The time unit.
* @return false if the timeout elapsed, true if the synch event we are waiting for had happened.
* @throws InterruptedException when the internal condition throws it.
*/
public boolean await(long timeout, TimeUnit timeUnit) throws InterruptedException {
return synchronizer.await(timeout, timeUnit);
}
// ====================================================================
// start of inner private class Synchronizer
// ====================================================================
/**
* This inner class serves to notify all interested parties that the ThreadPoolExecutor has finished running all the
* tasks given to its execute method.
*/
private class Synchronizer {
private final Lock lock = new ReentrantLock();
private final Condition done = lock.newCondition();
private boolean isDone = false;
/**
* This PRIVATE method allows the ThreadPoolExecutor to notify all interested parties that all tasks given to
* the execute method have run to conclusion.
*/
private void signalAll() {
lock.lock(); // MUST lock!
try {
isDone = true; // To help the await method ascertain that it has not waken up
// 'spuriously'
done.signalAll();
} finally {
lock.unlock(); // Make sure to unlock even in case of an exception
}
}
/**
* This is the inner implementation for supporting the NotifyingBlockingThreadPoolExecutor.await().
*
* @throws InterruptedException when the internal condition throws it.
* @see NotifyingBlockingThreadPoolExecutor#await() for details.
*/
public void await() throws InterruptedException {
lock.lock(); // MUST lock!
try {
while (!isDone) { // Ascertain that this is not a 'spurious wake-up'
done.await();
}
} finally {
isDone = false; // for next time
lock.unlock(); // Make sure to unlock even in case of an exception
}
}
/**
* Inner implementation for supporting the NotifyingBlockingThreadPoolExecutor.await(timeout, timeUnit).
*
* @param timeout The await time.
* @param timeUnit The await {@link TimeUnit}.
* @throws InterruptedException when the internal condition throws it.
* @see NotifyingBlockingThreadPoolExecutor#await(long, TimeUnit) for details.
*/
public boolean await(long timeout, TimeUnit timeUnit) throws InterruptedException {
boolean awaitResult = false;
lock.lock(); // MUST lock!
boolean localIsDone;
try {
awaitResult = done.await(timeout, timeUnit);
} finally {
localIsDone = isDone;
isDone = false; // for next time
lock.unlock(); // Make sure to unlock even in case of an exception
}
// make sure we return true only if done!
return awaitResult && localIsDone;
}
}
/**
* This Policy class enforces the blocking feature of the NotifyingBlockingThreadPoolExecutor. It does so by
* invoking the BlockingQueue's put method (instead of the offer method that is used by the standard implementation
* of the ThreadPoolExecutor - see the opened Java 6 source code).
*/
private static class BlockThenRunPolicy implements RejectedExecutionHandler {
private long maxBlockingTime;
private TimeUnit maxBlockingTimeUnit;
private Callable blockingTimeCallback;
BlockThenRunPolicy(long maxBlockingTime,
TimeUnit maxBlockingTimeUnit,
Callable blockingTimeCallback) {
this.maxBlockingTime = maxBlockingTime;
this.maxBlockingTimeUnit = maxBlockingTimeUnit;
this.blockingTimeCallback = blockingTimeCallback;
}
public BlockThenRunPolicy() {
// just keep the maxBlocking gang all null / 0
}
/**
* When this method is invoked by the ThreadPoolExecutor's reject method it simply asks for the Executor's Queue
* and calls on its put method which will Block (at least for the ArrayBlockingQueue).
*
* @see RejectedExecutionHandler#rejectedExecution(Runnable, ThreadPoolExecutor)
*/
@Override
public void rejectedExecution(Runnable task, ThreadPoolExecutor executor) {
BlockingQueue workQueue = executor.getQueue();
boolean taskSent = false;
while (!taskSent) {
if (executor.isShutdown()) {
throw new RejectedExecutionException(
"ThreadPoolExecutor has shutdown while attempting to offer a new task.");
}
try {
// check whether to offer (blocking) with a timeout or without
if (blockingTimeCallback != null) {
// put on the queue and block if no room is available, with a timeout
// the result of the call to offer says whether the task was accepted or not
if (workQueue.offer(task, maxBlockingTime, maxBlockingTimeUnit)) {
// task accepted
taskSent = true;
} else {
// task was not accepted - call the Callback
Boolean result;
try {
result = blockingTimeCallback.call();
} catch (Exception e) {
// we got an exception from the Callback, wrap it and throw
throw new RejectedExecutionException(e);
}
// if result if false we need to throw an exception
// otherwise, just continue with the loop
if (!result) {
throw new RejectedExecutionException("User decided to stop waiting for task insertion");
}
}
}
// no timeout
else {
// just send the task (blocking, if the queue is full)
workQueue.put(task);
// task accepted
taskSent = true;
}
} catch (InterruptedException e) {
// someone woke us up and we need to go back to the offer/put call...
}
} // end of while for InterruptedException
}
}
}