ExecutorService thread safety - java

Let's say I have an instance of ExecutorService from one of Executors static factory methods.
If I submit a Callable where RetVal is not a thread-safe, locally instantiated object from some thread, do I need to worry about RetVals' integrity when I get() it from the same thread? People say that local variables are thread-safe, but I am not sure if it applies when you're returning a locally instantiated Object and receiving it from some other thread.
Here's an example similar to my situation:
ExecutorService executor = Executors.newFixedThreadPool(5);
Future<List<String>> fut = executor.submit(() -> {
List<String> ret = new ArrayList<>();
ret.add("aasdf");
ret.add("dfls");
return ret;
});
List<String> myList = fut.get();
In the above example, I'm retrieving an ArrayList that was created in a different thread--one created by executor. I don't think above code is thread safe but I was not able to find much information regarding my specific situation.
Now I tried the above code on my computer and it actually returned the expected result 100% of the time I tried it, and I even tried with my own implementation of an ExecutorService and so far I have only got the expected results. So unless I have gotten extremely lucky I am pretty sure it works but I'm not sure how.
I created a not thread-safe object in another thread and received it in another; shouldn't I have a chance to have received a partially constructed object--in my case a list that does not contain 2 strings?
Below is my custom implementation I made just to test. You can ignore the EType enum thingy.
class MyExecutor {
enum EType {
NoHolder, Holder1, Holder2
}
private ConcurrentLinkedQueue<MyFutureTask<?>> tasksQ;
private final Thread thread;
private final EType eType;
public MyExecutor(EType eType) {
eType = Objects.requireNonNull(eType);
tasksQ = new ConcurrentLinkedQueue<>();
thread = new Thread(new MyRunnable());
thread.start();
}
public <T> Future<T> submit(Callable<T> c) {
MyFutureTask<T> task = new MyFutureTask<T>(c, eType);
tasksQ.add(task);
return task;
}
class MyRunnable implements Runnable {
#Override
public void run() {
while (true) {
if (tasksQ.isEmpty()) {
try {
Thread.sleep(1);
continue;
} catch (InterruptedException ite) {
Thread.interrupted();
break;
}
}
MyFutureTask<?> task = tasksQ.poll();
try {
task.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class MyFutureTask<T> implements RunnableFuture<T> {
final Callable<?> cb;
volatile Object outcome;
static final int STATE_PENDING = 1;
static final int STATE_EXECUTING = 2;
static final int STATE_DONE = 3;
final AtomicInteger atomicState = new AtomicInteger(STATE_PENDING);
final EType eType;
public MyFutureTask(Callable<?> cb, EType eType) {
cb = Objects.requireNonNull(cb);
eType = Objects.requireNonNull(eType);
}
#Override
public boolean cancel(boolean mayInterruptIfRunning) {
throw new NotImplementedException();
}
#Override
public boolean isCancelled() {
return false;
}
#Override
public boolean isDone() {
return atomicState.get() == STATE_DONE;
}
#SuppressWarnings("unchecked")
#Override
public T get() throws InterruptedException, ExecutionException {
while (true) {
switch (atomicState.get()) {
case STATE_PENDING:
case STATE_EXECUTING:
// Thread.sleep(1);
break;
case STATE_DONE:
return (T)outcome;
default:
throw new IllegalStateException();
}
}
}
#Override
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
throw new NotImplementedException();
}
void set(T t) {
outcome = t;
}
#Override
public void run() {
if (atomicState.compareAndSet(STATE_PENDING, STATE_EXECUTING)) {
Object result;
try {
switch (eType) {
case NoHolder:
result = cb.call();
break;
case Holder1:
throw new NotImplementedException();
case Holder2:
throw new NotImplementedException();
default:
throw new IllegalStateException();
}
} catch (Exception e) {
e.printStackTrace();
result = null;
}
outcome = result;
atomicState.set(STATE_DONE);
}
}
}
}
class MyTask implements Callable<List<Integer>> {
#Override
public List<Integer> call() throws Exception {
List<Integer> ret = new ArrayList<>(100);
IntStream.range(0, 100).boxed().forEach(ret::add);
return ret;
}
}

The important thing is the happens-before relationship. From ExecutorService API docs:
Memory consistency effects: Actions in a thread prior to the
submission of a Runnable or Callable task to an ExecutorService
happen-before any actions taken by that task, which in turn
happen-before the result is retrieved via Future.get().
So you are safe to transfer a mutable object like this. The ExecutorService implementation transfers the object via some form of safe publication.
Obviously, don't update the object in the original thread after returning it.
If you were to communicate between threads by stashing in a shared non-volatile field, then that would be unsafe.

Thread safety becomes a concern when multiple threads try to access and modify the same state simultaneously.
Note that you will not get hold of the actual result from a Future until the task is finished (i.e. Future#get will not return until the task is finished).
In your first example, thread safety is not an issue because the a new object (while mutable) is created by one thread (the thread created by the Executor) and retrieved from the Future object once that thread has finished processing the task. Once the calling thread gets hold of the object, it cannot be modified by any other thread, because the creating thread no longer has access to the List.

Related

Correct wait to fetch queue of threads

Ok, so, I have a java class in which every method must run in a thread.
only one thread is executed per time and at a specific time.
I've implemented like this, with a inner class that extends Thread.
private class MyOperation extends Thread {
public static final String M1 = "method1";
public static final String M2 = "method2";
public static final String M3 = "method3";
protected long scheduledStartTime = 0;
private String type;
public MyOperation(String type, long milliSecondsToWait) {
this.type = type;
scheduledStartTime = System.currentTimeMillis() + mlliSecondsToWait;
}
#Override
public void run() {
switch(type){
case M1:
doMethod1();
break;
case M2:
doMethod3();
break;
case M3:
doMethod3();
break;
}
setCurrentOperation(null);
}
}
private void setCurrentOperation(MyOperation task) {
synchronized (currentOperation) {
this.currentOperation = task;
}
}
then I have the Thread queue and the current running thread
private MyOperation currentOperation;
private Queue <MyOperation> operationList;
And I'm fetching tasks like this:
private void fetchTasks() {
new Thread() {
#Override
public void run() {
while(true) {
if(currentOperation == null && !operationList.isEmpty()) {
currentOperation = getOperation();
while(currentOperation.scheduledStartTime > System.currentTimeMillis()) {
// do nothing, wait for proper time;
}
currentOperation.start();
}
}
}
}.start();
}
private MyOperation getOperation() {
synchronized (operationList) {
return operationList.remove();
}
}
and I'm adding thread to the queue like this, for example:
addOperation(new MyOperation(M1, 5));
private void addOperation(MyOperation task) {
synchronized (operationList) {
operationList.add(task);
}
}
My questions are:
Is there a better way to run each method in a diffent thread?
Is this way of fetching the threads queue correct?
Thank you very much
Just a little thing: if your operationsList is empty or the currentOperation is not null your thread starts going in circles really fast.
You could use Thread.wait() and .notify() to avoid this.
Also you are using currentOperation with and without synchronized. This could get you into trouble.
Have you condsidered using a ScheduledExecutorService (java.util.concurrent) to schedule your tasks?

ForkJoinPool resets thread interrupted state

I just noticed the following phenomena when cancelling a Future returned by ForkJoinPool. Given the following example code:
ForkJoinPool pool = new ForkJoinPool();
Future<?> fut = pool.submit(new Callable<Void>() {
#Override
public Void call() throws Exception {
while (true) {
if (Thread.currentThread().isInterrupted()) { // <-- never true
System.out.println("interrupted");
throw new InterruptedException();
}
}
}
});
Thread.sleep(1000);
System.out.println("cancel");
fut.cancel(true);
The program never prints interrupted. The docs of ForkJoinTask#cancel(boolean) say:
mayInterruptIfRunning - this value has no effect in the default implementation because interrupts are not used to control cancellation.
If ForkJoinTasks ignore interrupts, how else are you supposed to check for cancellation inside Callables submitted to a ForkJoinPool?
This happens because Future<?> is a ForkJoinTask.AdaptedCallable which extends ForkJoinTask, whose cancel method is:
public boolean cancel(boolean mayInterruptIfRunning) {
return setCompletion(CANCELLED) == CANCELLED;
}
private int setCompletion(int completion) {
for (int s;;) {
if ((s = status) < 0)
return s;
if (UNSAFE.compareAndSwapInt(this, statusOffset, s, completion)) {
if (s != 0)
synchronized (this) { notifyAll(); }
return completion;
}
}
}
It does not do any interruptions, it just sets status. I suppose this happens becouse ForkJoinPools's Futures might have a very complicated tree structure, and it is unclear in which order to cancel them.
Sharing some more light on top of #Mkhail answer:
Using ForkJoinPool execute() instead of submit() will force a failed Runnable to throw a worker exception, and this exception will be caught by the Thread UncaughtExceptionHandler.
Taking from Java 8 code:
submit is using AdaptedRunnableAction().
execute is using RunnableExecuteAction() (see the rethrow(ex)).
/**
* Adaptor for Runnables without results
*/
static final class AdaptedRunnableAction extends ForkJoinTask<Void>
implements RunnableFuture<Void> {
final Runnable runnable;
AdaptedRunnableAction(Runnable runnable) {
if (runnable == null) throw new NullPointerException();
this.runnable = runnable;
}
public final Void getRawResult() { return null; }
public final void setRawResult(Void v) { }
public final boolean exec() { runnable.run(); return true; }
public final void run() { invoke(); }
private static final long serialVersionUID = 5232453952276885070L;
}
/**
* Adaptor for Runnables in which failure forces worker exception
*/
static final class RunnableExecuteAction extends ForkJoinTask<Void> {
final Runnable runnable;
RunnableExecuteAction(Runnable runnable) {
if (runnable == null) throw new NullPointerException();
this.runnable = runnable;
}
public final Void getRawResult() { return null; }
public final void setRawResult(Void v) { }
public final boolean exec() { runnable.run(); return true; }
void internalPropagateException(Throwable ex) {
rethrow(ex); // rethrow outside exec() catches.
}
private static final long serialVersionUID = 5232453952276885070L;
}

Executor wait for ever in a singleton class

I have implemented a singleton (manager) to manage some related tasks, inside this manager I am using an executor to handle 10 task at the same time, I was using linkedBlockingQueue with no limit, and that's working good so far, but now I need to set a limitation to my executor queue because I have a lot of tasks (hundreds of thousands tasks), and I don’t want to put them all in my queue that causing me a performance issues, so what I have done:
here is my Executor :
public class MyThreadPoolExecutor extends ThreadPoolExecutor {
public MyThreadPoolExecutor(int corePoolSize, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, corePoolSize + 5, 500, TimeUnit.MILLISECONDS, workQueue);
}
#Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
//Do something to my task
}
#Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if(t != null) {
//
} else {
//Do something to my task
}
}
}
and here is my manager :
public final class MyManager {
private static MyManager manager = new MyManager();
public static final int queueMaxSize = 100;
private BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(queueMaxSize);
private ExecutorService executor = new MyThreadPoolExecutor(10, workQueue);
/**
* constructor
*/
private MyManager() {}
public static MyManager getInstance(){
if (manager == null){
synchronized(MyManager.class){
if (manager == null){
manager = new MyManager();
}
}
}
return manager;
}
/**
*/
public void executeTask(Integer key){
executeTask(key, Locale.getDefault());
}
/**
*/
public void executeTask(Integer key, Locale locale) {
Tasker task = new Tasker(key, locale);
executor.execute(task);
}
}
and here the class that asking to do the tasks :
public class MyClass {
public void doTasks() {
//geting my tasks in array of list, its holding more than 900 000 tasks,
//sometimes its holding up to 10 million task like :
MyManager.getInstance().isFull() {\\wait, then ask again}
ArrayList<Integer> myTasks = getAllTasksIds();
for(Integer id : myTasks) {
//if i perform a waiting here it will be waiting for ever.
MyManaget.getInstance().executeTask(id);
}
}
}
What I want exactly to wait the executor until finish his queue tasks, then re-full it again.
But the problem is when I try to wait based on queue size, the executor won’t work, and its wait forever because the queue still full.
Why wouldn't you just use a bounded blocking queue (i.e. specify a bound of a BlockingQueue)? If you use a bounded blocking queue (of which size you can choose yourself), your producer will block when the queue is full, and will resume publishing tasks when a task is consumed from a queue. This way, you can avoid putting too much stuff too quickly onto the queue, but also avoid putting too less on the queue. That's kind of the point of blocking queues...
I tested your code but instead of using ArrayBlockingQueue I extended it with this... And it works. Try it:
public static class MyBlockingQueue extends ArrayBlockingQueue<Runnable> {
private static final long serialVersionUID= -9016421283603545618L;
public static Lock lock= new ReentrantLock();
public static Condition condition= lock.newCondition();
public static volatile Boolean isWaiting= false;
public MyBlockingQueue(int capacity) {
super(capacity, true);
}
#Override
public boolean offer(Runnable e) {
if (remainingCapacity() == 0) {
try {
isWaiting= true;
condition.await();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
return super.offer(e);
}
#Override
public Runnable take() throws InterruptedException {
Runnable take= super.take();
if (remainingCapacity() > 0 && isWaiting) {
isWaiting= false;
condition.signal();
}
return take;
}
}

Am I using callable and futures multithreading the right way (java)?

I need some tasks to be done multi threaded.
I know in advance that i will continue with my program when all tasks are completed.
Is the following code right for this purpose?
public void test() {
Callable<String> myCall = new Callable() {
#Override
public String call() throws Exception {
return doDomething();
}
};
Callable<String> myCall2 = new Callable() {
#Override
public String call() throws Exception {
return doDomething2();
}
};
ExecutorService executor = Executors.newFixedThreadPool(2);
List<Callable<String>> list = Arrays.asList(myCall,myCall2);
List<Future<String>> futuresList = executor.invokeAll(list);
String result1 = futuresList.get(0).get();
String result2 = futuresList.get(0).get();
//...
}
I'm trying to change this to work with generics:
public void test() {
Callable<?> myCall = new Callable() {
#Override
public String call() throws Exception {
return doDomething();
}
};
Callable<?> myCall2 = new Callable() {
#Override
public String call() throws Exception {
return doDomething2();
}
};
ExecutorService executor = Executors.newFixedThreadPool(2);
List<Callable<?>> list = Arrays.asList(myCall,myCall2);
List<Future<?>> futuresList = executor.invokeAll((Collection<? extends Callable<?>>)list);
String result1 = futuresList.get(0).get();
String result2 = futuresList.get(0).get();
// ...
}
I get the following compilation error:
The method invokeAll(Collection<? extends Callable<T>>) in the type ExecutorService is not applicable for the arguments (Collection<capture#2-of ? extends Callable<?>>).
Aside from you accessing the zeroth index twice I dont see anything wrong with it.
This edit is in regards to your question on how to implement it without the ExecutorService. As ColinD notes, you really shouldn't I will show why
To get the same set of functionality you would need two Objects and use two threads as a latch
String result1 = null;
String result2 = null;
public void test() {
Thread thread1 = new Thread(new Runnable(){
public void run(){
result1 = doSomething();
}
});
Thread thread2 = new Thread(new Runnable(){
public void run(){
result2 = doSomething2();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
...
...
}
Now what if you want to add another thread/unit-of-work then you need
Thread thread3 = new Thread(new Runnable(){
public void run(){
result3 = doSomething3();
}
});
thread3.start();
thread3.join();
And so forth. Hence your solution is the better way to do this.
Well, let me be devil's advocate then.
The only thing in this example that actually can hit right back at you is the fact that you are calling two methods a-sychronously which is probably not that obvious to other programmers.
I guess this is just an example, but other programmers might not recognize that doSomething and doSomething2 should not have 'shared mutable state' since they are executed a-synchronously. It's not that obvious in this setup.
So I would recommend to actually put the logic in separate classes.

Controlling Task execution order with ExecutorService

I have a process which delegates asynch tasks to a pool of threads. I need to ensure that certain tasks are executed in order.
So for example
Tasks arrive in order
Tasks a1, b1, c1, d1 , e1, a2, a3, b2, f1
Tasks can be executed in any order except where there is a natural dependancy, so a1,a2,a3 must be processed in that order by either allocating to the same thread or blocking these until I know the previous a# task was completed.
Currently it doesn't use the Java Concurrency package, but I'm considering changing to take avantage of the thread management.
Does anyone have a similar solution or suggestions of how to achieve this
I write own Executor that warrants task ordering for tasks with same key. It uses map of queues for order tasks with same key. Each keyed task execute next task with the same key.
This solution don't handle RejectedExecutionException or other exceptions from delegated Executor! So delegated Executor should be "unlimited".
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.Executor;
/**
* This Executor warrants task ordering for tasks with same key (key have to implement hashCode and equal methods correctly).
*/
public class OrderingExecutor implements Executor{
private final Executor delegate;
private final Map<Object, Queue<Runnable>> keyedTasks = new HashMap<Object, Queue<Runnable>>();
public OrderingExecutor(Executor delegate){
this.delegate = delegate;
}
#Override
public void execute(Runnable task) {
// task without key can be executed immediately
delegate.execute(task);
}
public void execute(Runnable task, Object key) {
if (key == null){ // if key is null, execute without ordering
execute(task);
return;
}
boolean first;
Runnable wrappedTask;
synchronized (keyedTasks){
Queue<Runnable> dependencyQueue = keyedTasks.get(key);
first = (dependencyQueue == null);
if (dependencyQueue == null){
dependencyQueue = new LinkedList<Runnable>();
keyedTasks.put(key, dependencyQueue);
}
wrappedTask = wrap(task, dependencyQueue, key);
if (!first)
dependencyQueue.add(wrappedTask);
}
// execute method can block, call it outside synchronize block
if (first)
delegate.execute(wrappedTask);
}
private Runnable wrap(Runnable task, Queue<Runnable> dependencyQueue, Object key) {
return new OrderedTask(task, dependencyQueue, key);
}
class OrderedTask implements Runnable{
private final Queue<Runnable> dependencyQueue;
private final Runnable task;
private final Object key;
public OrderedTask(Runnable task, Queue<Runnable> dependencyQueue, Object key) {
this.task = task;
this.dependencyQueue = dependencyQueue;
this.key = key;
}
#Override
public void run() {
try{
task.run();
} finally {
Runnable nextTask = null;
synchronized (keyedTasks){
if (dependencyQueue.isEmpty()){
keyedTasks.remove(key);
}else{
nextTask = dependencyQueue.poll();
}
}
if (nextTask!=null)
delegate.execute(nextTask);
}
}
}
}
When I've done this in the past I've usually had the ordering handled by a component which then submits callables/runnables to an Executor.
Something like.
Got a list of tasks to run, some with dependencies
Create an Executor and wrap with an ExecutorCompletionService
Search all tasks, any with no dependencies, schedule them via the completion service
Poll the completion service
As each task completes
Add it to a "completed" list
Reevaluate any waiting tasks wrt to the "completed list" to see if they are "dependency complete". If so schedule them
Rinse repeat until all tasks are submitted/completed
The completion service is a nice way of being able to get the tasks as they complete rather than trying to poll a bunch of Futures. However you will probably want to keep a Map<Future, TaskIdentifier> which is populated when a task is schedule via the completion service so that when the completion service gives you a completed Future you can figure out which TaskIdentifier it is.
If you ever find yourself in a state where tasks are still waiting to run, but nothing is running and nothing can be scheduled then your have a circular dependency problem.
When you submit a Runnable or Callable to an ExecutorService you receive a Future in return. Have the threads that depend on a1 be passed a1's Future and call Future.get(). This will block until the thread completes.
So:
ExecutorService exec = Executor.newFixedThreadPool(5);
Runnable a1 = ...
final Future f1 = exec.submit(a1);
Runnable a2 = new Runnable() {
#Override
public void run() {
f1.get();
... // do stuff
}
}
exec.submit(a2);
and so on.
You can use Executors.newSingleThreadExecutor(), but it will use only one thread to execute your tasks. Another option is to use CountDownLatch. Here is a simple example:
public class Main2 {
public static void main(String[] args) throws InterruptedException {
final CountDownLatch cdl1 = new CountDownLatch(1);
final CountDownLatch cdl2 = new CountDownLatch(1);
final CountDownLatch cdl3 = new CountDownLatch(1);
List<Runnable> list = new ArrayList<Runnable>();
list.add(new Runnable() {
public void run() {
System.out.println("Task 1");
// inform that task 1 is finished
cdl1.countDown();
}
});
list.add(new Runnable() {
public void run() {
// wait until task 1 is finished
try {
cdl1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task 2");
// inform that task 2 is finished
cdl2.countDown();
}
});
list.add(new Runnable() {
public void run() {
// wait until task 2 is finished
try {
cdl2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task 3");
// inform that task 3 is finished
cdl3.countDown();
}
});
ExecutorService es = Executors.newFixedThreadPool(200);
for (int i = 0; i < 3; i++) {
es.submit(list.get(i));
}
es.shutdown();
es.awaitTermination(1, TimeUnit.MINUTES);
}
}
Another option is to create your own executor, call it OrderedExecutor, and create an array of encapsulated ThreadPoolExecutor objects, with 1 thread per internal executor. You then supply a mechanism for choosing one of the internal objects, eg, you can do this by providing an interface that the user of your class can implement:
executor = new OrderedExecutor( 10 /* pool size */, new OrderedExecutor.Chooser() {
public int choose( Runnable runnable ) {
MyRunnable myRunnable = (MyRunnable)runnable;
return myRunnable.someId();
});
executor.execute( new MyRunnable() );
The implementation of OrderedExecutor.execute() will then use the Chooser to get an int, you mod this with the pool size, and that's your index into the internal array. The idea being that "someId()" will return the same value for all the "a's", etc.
I created an OrderingExecutor for this problem. If you pass the same key to to method execute() with different runnables, the execution of the runnables with the same key will be in the order the execute() is called and will never overlap.
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executor;
/**
* Special executor which can order the tasks if a common key is given.
* Runnables submitted with non-null key will guaranteed to run in order for the same key.
*
*/
public class OrderedExecutor {
private static final Queue<Runnable> EMPTY_QUEUE = new QueueWithHashCodeAndEquals<Runnable>(
new ConcurrentLinkedQueue<Runnable>());
private ConcurrentMap<Object, Queue<Runnable>> taskMap = new ConcurrentHashMap<Object, Queue<Runnable>>();
private Executor delegate;
private volatile boolean stopped;
public OrderedExecutor(Executor delegate) {
this.delegate = delegate;
}
public void execute(Runnable runnable, Object key) {
if (stopped) {
return;
}
if (key == null) {
delegate.execute(runnable);
return;
}
Queue<Runnable> queueForKey = taskMap.computeIfPresent(key, (k, v) -> {
v.add(runnable);
return v;
});
if (queueForKey == null) {
// There was no running task with this key
Queue<Runnable> newQ = new QueueWithHashCodeAndEquals<Runnable>(new ConcurrentLinkedQueue<Runnable>());
newQ.add(runnable);
// Use putIfAbsent because this execute() method can be called concurrently as well
queueForKey = taskMap.putIfAbsent(key, newQ);
if (queueForKey != null)
queueForKey.add(runnable);
delegate.execute(new InternalRunnable(key));
}
}
public void shutdown() {
stopped = true;
taskMap.clear();
}
/**
* Own Runnable used by OrderedExecutor.
* The runnable is associated with a specific key - the Queue<Runnable> for this
* key is polled.
* If the queue is empty, it tries to remove the queue from taskMap.
*
*/
private class InternalRunnable implements Runnable {
private Object key;
public InternalRunnable(Object key) {
this.key = key;
}
#Override
public void run() {
while (true) {
// There must be at least one task now
Runnable r = taskMap.get(key).poll();
while (r != null) {
r.run();
r = taskMap.get(key).poll();
}
// The queue emptied
// Remove from the map if and only if the queue is really empty
boolean removed = taskMap.remove(key, EMPTY_QUEUE);
if (removed) {
// The queue has been removed from the map,
// if a new task arrives with the same key, a new InternalRunnable
// will be created
break;
} // If the queue has not been removed from the map it means that someone put a task into it
// so we can safely continue the loop
}
}
}
/**
* Special Queue implementation, with equals() and hashCode() methods.
* By default, Java SE queues use identity equals() and default hashCode() methods.
* This implementation uses Arrays.equals(Queue::toArray()) and Arrays.hashCode(Queue::toArray()).
*
* #param <E> The type of elements in the queue.
*/
private static class QueueWithHashCodeAndEquals<E> implements Queue<E> {
private Queue<E> delegate;
public QueueWithHashCodeAndEquals(Queue<E> delegate) {
this.delegate = delegate;
}
public boolean add(E e) {
return delegate.add(e);
}
public boolean offer(E e) {
return delegate.offer(e);
}
public int size() {
return delegate.size();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
public boolean contains(Object o) {
return delegate.contains(o);
}
public E remove() {
return delegate.remove();
}
public E poll() {
return delegate.poll();
}
public E element() {
return delegate.element();
}
public Iterator<E> iterator() {
return delegate.iterator();
}
public E peek() {
return delegate.peek();
}
public Object[] toArray() {
return delegate.toArray();
}
public <T> T[] toArray(T[] a) {
return delegate.toArray(a);
}
public boolean remove(Object o) {
return delegate.remove(o);
}
public boolean containsAll(Collection<?> c) {
return delegate.containsAll(c);
}
public boolean addAll(Collection<? extends E> c) {
return delegate.addAll(c);
}
public boolean removeAll(Collection<?> c) {
return delegate.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
return delegate.retainAll(c);
}
public void clear() {
delegate.clear();
}
#Override
public boolean equals(Object obj) {
if (!(obj instanceof QueueWithHashCodeAndEquals)) {
return false;
}
QueueWithHashCodeAndEquals<?> other = (QueueWithHashCodeAndEquals<?>) obj;
return Arrays.equals(toArray(), other.toArray());
}
#Override
public int hashCode() {
return Arrays.hashCode(toArray());
}
}
}
In Habanero-Java library, there is a concept of data-driven tasks which can be used to express dependencies between tasks and avoid thread-blocking operations. Under the covers Habanero-Java library uses the JDKs ForkJoinPool (i.e. an ExecutorService).
For example, your use case for tasks A1, A2, A3, ... could be expressed as follows:
HjFuture a1 = future(() -> { doA1(); return true; });
HjFuture a2 = futureAwait(a1, () -> { doA2(); return true; });
HjFuture a3 = futureAwait(a2, () -> { doA3(); return true; });
Note that a1, a2, and a3 are just references to objects of type HjFuture and can be maintained in your custom data structures to specify the dependencies as and when the tasks A2 and A3 come in at runtime.
There are some tutorial slides available.
You can find further documentation as javadoc, API summary and primers.
I have written my won executor service which is sequence aware. It sequences the tasks which contain certain related reference and currently inflight.
You can go through the implementation at https://github.com/nenapu/SequenceAwareExecutorService

Categories

Resources