Simulate the multi-thread process of task T by using Future Class - java

Here is a task T:
T is composed of many subtasks, and the completion time of each subtask is different.If one of the subtasks fail, the others should be stoped at once and the task T will fail.
So how to simulate the process of task T? (require fast-failure)
Maybe Future Class could solve it. But how?

The easiest fix to this is to separate out these Runnables into their own threadpool so you can then call shutdownNow() on that pool only which interrupts all of the tasks in that pool

Here's an example using FutureTask.
One of the tasks simulates failure by waiting for 1 second and then cancelling the other tasks. Even though they are waiting for 5 seconds, the program completes in 1 second.
You would need to wrap your Runnable in something like this which can cancel if the actual task fails.
To be able to cancel a task, it needs to be in a state where Thread.interrupt() will stop the thread. If it is just sitting in a loop, for instance, then the Future will be cancelled (so get() will return immediately), but the task itself will keep running.
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
public class FutureTaskExample {
public static void main(String[] args) throws InterruptedException {
List<FutureTask<String>> tasks = new ArrayList<>();
ExecutorService executorService = Executors.newFixedThreadPool(10);
long start = System.currentTimeMillis();
for (int i = 0; i < 10; ++i) {
final int ii = i;
FutureTask<String> task =
new FutureTask<>(new Callable<String>() {
#Override
public String call() throws Exception {
if (ii == 9) {
Thread.sleep(1000);
tasks.subList(0,9).stream().forEach(t -> t.cancel(true));
return "Failure";
} else {
long start = System.currentTimeMillis();
Thread.sleep(5000);
System.out.println("Task " + ii + " slept for " + (System.currentTimeMillis() - start));
return "Completed";
}
}
});
tasks.add(task);
executorService.execute(task);
}
List<String> results = tasks.stream().map(t -> {
try {
return t.get();
} catch (Exception e) {
// ignore
e.printStackTrace();
return "Interrupted";
}
}).collect(Collectors.toList());
System.out.println("Completed in " + (System.currentTimeMillis() - start) + " " + results);
executorService.shutdown();
executorService.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("Done in " + (System.currentTimeMillis() - start));
}
}

I give a simulation example by using CompletableFuture
I create a Task Class TaskT, make it looks more real. And the Task Class contains three state, runTask() method and cancel() method.
Three state are Success, Cancelling and Cancelled.Success means task run completed.Cancelling means task is cancelling. Cancelled means the task has been Cancelled, advice others task cancel quickly.
runTask() method: I use Thread.sleep(interval) to simulate runing state. And when the task run finish, the code if(cancelled) return Result.CANCELLED; detect whether the state is canceled?
cancel() method: I use double-check lock to decorate the "cancel" logic to ensure it is single instance.
Finally, in the main metod, I use CompletableFuture Class to start thread and return param. thenAccept() method is useful and help SUCCESS TASK advice the other RUNNING TASKs cancel the task.
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class Competable {
// three state after run task
static enum Result{
SUCCESS, FAIL, CANCELLED
}
// task list
static List<TaskT> tasks = new ArrayList<>();
/**
* Task List
*/
public static class TaskT{
private String name;
private int timeInSecond;
private Result ret;
volatile boolean cancelling = false;
volatile boolean cancelled = false;
public TaskT(String name, int timeInSecond, Result ret){
this.name = name;
this.timeInSecond = timeInSecond * 1000;
this.ret = ret;
}
/**
* Simulate task runing
* runing time in real work is uncertain
* maybe run in computing,maybe run in IO
*/
public Result runTask(){
int interval = 100;
int total = 0;
try {
for(;;){
Thread.sleep(interval);
total += interval;
if(total>=timeInSecond) break;
if(cancelled) return Result.CANCELLED;
}
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(name + "Task End!!!");
return ret;
}
/**
* Simlulate task cancel
* and set cancel time
*/
public void cancel() {
if (!cancelled) {
synchronized (this) {
if (cancelled) return;
cancelling = true;
System.out.println(name + "cancelling!!!");
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "cancelled!!!");
}
cancelled = true;
}
}
}
/**
* rollback: advice the other thread cancel
*/
public static void callback(Result result, TaskT task){
if(Result.FAIL == result){
for(TaskT _task : tasks){
if(_task!=task){
_task.cancel();
}
}
}
}
public static void main(String[] args) throws IOException {
TaskT subtask1 = new TaskT("task1", 3, Result.SUCCESS);
TaskT subtask2 = new TaskT("task2", 4, Result.SUCCESS);
TaskT subtask3 = new TaskT("task3", 1, Result.FAIL);
tasks.add(subtask1);
tasks.add(subtask2);
tasks.add(subtask3);
for(TaskT task:tasks){
CompletableFuture f = CompletableFuture.supplyAsync(()->task.runTask())
.thenAccept((result -> callback(result, task)));
}
// System.in.read();
}
}

Related

Tasks not waiting threads available on ThreadPool [duplicate]

This question already has answers here:
ThreadPoolExecutor Block When its Queue Is Full?
(10 answers)
Closed 3 months ago.
We have a large text file in which each line requires intensive process. The design is to have a class that reads the file and delegates the processing of each line to a thread, via thread pool. The file reader class should be blocked from reading the next line once there is no free thread in the pool to do the processing. So i need a blocking thread pool
In the current implementation ThreadPoolExecutor.submit() and ThreadPoolExecutor.execute() methods throw RejectedExecutionException exception after the configured # of threads get busy as i showed in code snippet below.
public class BlockingTp {
public static void main(String[] args) {
BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
ThreadPoolExecutor executorService=
new ThreadPoolExecutor(1, 3, 30, TimeUnit.SECONDS, blockingQueue);
int Jobs = 10;
System.out.println("Starting application with " + Jobs + " jobs");
for (int i = 1; i <= Jobs; i++)
try {
executorService.submit(new WorkerThread(i));
System.out.println("job added " + (i));
} catch (RejectedExecutionException e) {
System.err.println("RejectedExecutionException");
}
}
}
class WorkerThread implements Runnable {
int job;
public WorkerThread(int job) {
this.job = job;
}
public void run() {
try {
Thread.sleep(1000);
} catch (Exception excep) {
}
}
}
Output of above program is
Starting application to add 10 jobs
Added job #1
Added job #2
Added job #3
Added job #4
Added job #5
Added job #6
RejectedExecutionException
RejectedExecutionException
RejectedExecutionException
RejectedExecutionException
Can some one throw some light i.e how i can implement blocking thread pool.
Can some one throw some light i.e how i can implement blocking thread pool.
You need to set a rejection execution handler on your executor service. When the thread goes to put the job into the executor, it will block until there is space in the blocking queue.
BlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
ThreadPoolExecutor executorService =
new ThreadPoolExecutor(1, 3, 30, TimeUnit.SECONDS, arrayBlockingQueue);
// when the blocking queue is full, this tries to put into the queue which blocks
executorService.setRejectedExecutionHandler(new RejectedExecutionHandler() {
#Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
// block until there's room
executor.getQueue().put(r);
// check afterwards and throw if pool shutdown
if (executor.isShutdown()) {
throw new RejectedExecutionException(
"Task " + r + " rejected from " + executor);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RejectedExecutionException("Producer interrupted", e);
}
}
});
So instead of the TRE throwing a RejectedExecutionException, it will call the rejection handler which will in turn try to put the job back on the queue. This blocks the caller.
Lets have a look at your code again:
for (int i = 1; i <= Jobs; i++)
try {
tpExe.submit(new WorkerThread(i));
System.out.println("job added " + (i));
} catch (RejectedExecutionException e) {
System.err.println("RejectedExecutionException");
}
So - when you try to submit, and the pool is busy, that exception is thrown. If you want to wrap around that, it could look like:
public void yourSubmit(Runnable whatever) {
boolean submitted = false;
while (! submitted ) {
try {
tpExe.submit(new WorkerThread(whatever));
submitted = true;
} catch (RejectedExecutionException re) {
// all threads busy ... so wait some time
Thread.sleep(1000);
}
In other words: use that exception as "marker" that submits are currently not possible.
You can use semaphore for to control the resource.Reader will read and create asynchronous task by acquiring semaphore.If every thread is busy the reader thread will wait till thread is available.
public class MyExecutor {
private final Executor exec;
private final Semaphore semaphore;
public BoundedExecutor(Executor exec, int bound) {
this.exec = exec;
this.semaphore = new Semaphore(bound);
}
public void submitTask(final Runnable command)
throws InterruptedException, RejectedExecutionException {
semaphore.acquire();
try {
exec.execute(new Runnable() {
public void run() {
try {
command.run();
} finally {
semaphore.release();
}
}
});
} catch (RejectedExecutionException e) {
semaphore.release();
throw e;
}
}
}
Here is a RejectedExecutionHandler that supports the desired behavior. Unlike other implementations, it does not interact with the queue directly so it should be compatible with all Executor implementations and will not deadlock.
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.BiFunction;
import static com.github.cowwoc.requirements.DefaultRequirements.assertThat;
import static com.github.cowwoc.requirements.DefaultRequirements.requireThat;
/**
* Applies a different rejection policy depending on the thread that requested execution.
*/
public final class ThreadDependantRejectionHandler implements RejectedExecutionHandler
{
private final ThreadLocal<Integer> numberOfRejections = ThreadLocal.withInitial(() -> 0);
private final BiFunction<Thread, Executor, Action> threadToAction;
/**
* #param threadToAction indicates what action a thread should take when execution is rejected
* #throws NullPointerException if {#code threadToAction} is null
*/
public ThreadDependantRejectionHandler(BiFunction<Thread, Executor, Action> threadToAction)
{
requireThat(threadToAction, "threadToAction").isNotNull();
this.threadToAction = threadToAction;
}
#SuppressWarnings("BusyWait")
#Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
{
if (executor.isShutdown())
return;
Thread currentThread = Thread.currentThread();
Action action = threadToAction.apply(currentThread, executor);
if (action == Action.RUN)
{
r.run();
return;
}
if (action == Action.REJECT)
{
throw new RejectedExecutionException("The thread pool queue is full and the current thread is not " +
"allowed to block or run the task");
}
assertThat(action, "action").isEqualTo(Action.BLOCK);
int numberOfRejections = this.numberOfRejections.get();
++numberOfRejections;
this.numberOfRejections.set(numberOfRejections);
if (numberOfRejections > 1)
return;
try
{
ThreadLocalRandom random = ThreadLocalRandom.current();
while (!executor.isShutdown())
{
try
{
Thread.sleep(random.nextInt(10, 1001));
}
catch (InterruptedException e)
{
throw new WrappingException(e);
}
executor.submit(r);
numberOfRejections = this.numberOfRejections.get();
if (numberOfRejections == 1)
{
// Task was accepted, or executor has shut down
return;
}
// Task was rejected, reset the counter and try again.
numberOfRejections = 1;
this.numberOfRejections.set(numberOfRejections);
}
throw new RejectedExecutionException("Task " + r + " rejected from " + executor + " because " +
"the executor has been shut down");
}
finally
{
this.numberOfRejections.set(0);
}
}
public enum Action
{
/**
* The thread should run the task directly instead of waiting for the executor.
*/
RUN,
/**
* The thread should block until the executor is ready to run the task.
*/
BLOCK,
/**
* The thread should reject execution of the task.
*/
REJECT
}
}
This works for me.
class handler implements RejectedExecutionHandler{
#Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
executor.getQueue().put(r);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

Call method after each cycle of ScheduledExecutorService

I need the following scenario:
Run all ScheduledFutures within a cycle and call every time the method tasksCompleted() after all tasks finished its execution. The next scheduling cycle must not wait while calling tasksCompleted() after the actual scheduling cycle.
In short: Call a method after the completion of the actual scheduling-cycle and do not stop the next scheduling-cycle
The following code creates tasks and the scheduling works. However, I am not able call tasksCompleted() when all tasks within a cycle completed.
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class Scheduler {
public static void main(String[] args) {
final ScheduledExecutorService ses = Executors.newScheduledThreadPool(10);
System.out.println("- [" + LocalTime.now() + "] run parent-task...");
// create 3 tasks: each task needs 7 seconds.
var tasks = createTasks("child", 3, 7);
List<ScheduledFuture<?>> futures = new ArrayList<>();
tasks.forEach(t ->
{
ScheduledFuture<?> future = ses.scheduleWithFixedDelay(t, 0, 2, TimeUnit.SECONDS);
futures.add(future);
});
// this does not work..
var scheduleCycleCompleted = futures.stream().allMatch(f -> f.isDone());
System.out.println("scheduleCycleCompleted: " + scheduleCycleCompleted);
// maybe a solution with CompletableFuture?
CompletableFuture[] cfs = futures.toArray(new CompletableFuture[futures.size()]);
}
static void tasksCompleted() {
System.out.println("schedule cycle completed");
}
static List<Runnable> createTasks(String group, int numbersOfTasks, long taskDuration) {
var tasks = new ArrayList<Runnable>();
for (var i = 0; i < numbersOfTasks; i++) {
int taskNr = i;
Runnable task = () ->
{
System.out.println("- [" + LocalTime.now() + "] Running " + group + "-task" + taskNr + "...[needs "
+ taskDuration + " seconds]");
try {
TimeUnit.SECONDS.sleep(taskDuration);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
};
tasks.add(task);
}
return tasks;
}
}
Updated
I hope it will work.
CountDownLatch will solve the problem here.
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class Scheduler {
public static void main(String[] args) throws InterruptedException {
final ScheduledExecutorService ses = Executors.newScheduledThreadPool(10);
System.out.println("- [" + LocalTime.now() + "] run parent-task...");
int noOfTask=3;
CountDownLatch countDownLatch = new CountDownLatch(noOfTask);
TaskComplete taskComplete=new TaskCompleteImpl(noOfTask,countDownLatch);
// create 3 tasks: each task needs 7 seconds.
List<Runnable> tasks = createTasks("child", noOfTask, 7,countDownLatch,taskComplete);
List<ScheduledFuture<?>> futures = new ArrayList<>();
tasks.forEach(t ->
{
ScheduledFuture<?> future = ses.scheduleWithFixedDelay(t, 0, 2, TimeUnit.SECONDS);
futures.add(future);
});
// this does not work..
}
interface TaskComplete{
void tasksCompleted();
}
static class TaskCompleteImpl implements TaskComplete {
int totalTask=0;
int index=0;
CountDownLatch countDownLatch;
public TaskCompleteImpl(int totalTask){
}
public TaskCompleteImpl(int noOfTask, CountDownLatch countDownLatch) {
this.totalTask=noOfTask;
this.countDownLatch=countDownLatch;
}
#Override
public synchronized void tasksCompleted() {
index=index+1;
if(index==totalTask){
System.out.println("schedule cycle completed");
index=0;
countDownLatch=new CountDownLatch(totalTask);
}
}
}
static List<Runnable> createTasks(String group, int numbersOfTasks, long taskDuration, CountDownLatch countDownLatch, TaskComplete taskComplete) {
List tasks = new ArrayList<Runnable>();
for (int i = 0; i < numbersOfTasks; i++) {
int taskNr = i;
Runnable task = () ->
{
System.out.println("- [" + LocalTime.now() + "] Running " + group + "-task" + taskNr + "...[needs "
+ taskDuration + " seconds]");
try {
TimeUnit.SECONDS.sleep(taskDuration);
countDownLatch.countDown();
countDownLatch.await();
taskComplete.tasksCompleted();
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
};
tasks.add(task);
}
return tasks;
}
}
To handle the case of different runtimes within a scheduling cycle, what you need is a way to identify which task belongs to which group. You can do that by giving them an identifying counter, so that each time a task is executed, the counter is used to denominate the group it's running in.
interface GroupedRunnable extends Runnable {
String getGroup();
}
class CountingRunnable implements GroupedRunnable {
private AtomicInteger counter = new AtomicInteger();
private final Runnable delegate;
private final String taskName;
CountingRunnable(Runnable delegate, String taskName) {
this.delegate = delegate;
this.taskName = taskName;
}
public void run() {
System.out.printf("[%s] - Running task %s in group %s%n", LocalTime.now(), taskName, getGroup());
delegate.run();
counter.incrementAndGet();
System.out.printf("[%s] - Running task %s in group %s finished%n", LocalTime.now(), taskName, getGroup());
}
#Override
public String getGroup() {
return counter.toString();
}
}
Now, you can have a watchdog class that keeps track on which members of which group have already executed. Since you know how many members a group has, a simple counter is sufficient.
class GroupMonitoringService {
// key: group, value: tasks
Map<String, AtomicInteger> finishedTasks = new HashMap<>();
private final Runnable finisher;
private final int groupSize;
GroupMonitoringService(Runnable finisher, int groupSize) {
this.finisher = finisher;
this.groupSize = groupSize;
}
public synchronized void taskFinished(String group) {
var finishedInGroup = finishedTasks.computeIfAbsent(group, k -> new AtomicInteger());
if (finishedInGroup.incrementAndGet() >= groupSize) {
// scheduling group complete
System.out.printf("Group %s finished executing%n", group);
finisher.run();
finishedTasks.remove(group);
}
}
}
Now all you have to do is wrap your original task into a grouped task, and make sure the monitoring service is notified when it's finished, so wrap again.
private static List<Runnable> createTasks() {
List<Runnable> result = new ArrayList<>();
for (int i = 0; i < GROUP_SIZE; i++) {
RandomWaitTask originalTask = new RandomWaitTask();
CountingRunnable groupedTask = new CountingRunnable(originalTask, "Task " + i);
Runnable notifyingRunnable = () -> {
groupedTask.run();
MONITORING_SERVICE.taskFinished(groupedTask.getGroup());
};
result.add(notifyingRunnable);
}
return result;
}
So now you can just schedule those.
You can see the entire code here (although it doesn't actually run properly on that site because of the resource limit it imposes, but if you copy it into your IDE, it works).

How to kill asynchronously running thread after timeout in java 8 and implications of doing that

I am running an infinite loop and need to achieve the following steps:
check available threads in the executor service (the infinite loop)
fetches the task in the loop.
execute the task in the background(non-blocking) and kill the thread executing the task if it takes more than 3 seconds.
I have looked into the future get API that takes a timeout parameter but not this is blocking in nature.
while(any thread available in thread pool){
Task task = fetchTask();
// somehow execute this task in a non-blocking fashion with a timeout.
}
Is there a way to kill the asynchronously executing threads after the timeout?
Will the thread execution will stop and resources will be freed after the timeout?
To achieve this behavior you need this :
Custom class that extends the Thread class and implements the
Runnable interface
A Thread Executor to simply have asynchronous execution of threads
The Custom class let us named it 'Task' can have a special implemetation as follow :
import java.util.Date;
import java.util.concurrent.Callable;
public class Task implements Callable<String> {
private String name;
private Long elapsedTimeInMillSeconds = 0L;
static int counter = 0;
public Task(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
#Override
public String call() throws Exception {
Long startTimeInNanoSeconds, endTimeInNanoSeconds;
startTimeInNanoSeconds = System.nanoTime();
System.out.println("Executing : " + name + ", Current Seconds : " + new Date().getSeconds());
counter++;
System.out.println("Counter = " + counter + " for thread number " + name);
// Check if our logic is working as expected for Task2 we are going to delay for
// 7 seconds
if (counter == 2)
Thread.sleep(7000);
endTimeInNanoSeconds = System.nanoTime();
elapsedTimeInMillSeconds = (endTimeInNanoSeconds - startTimeInNanoSeconds) / 10000;
System.out
.println("Thread [ name : " + name + ", elapsed time : " + this.elapsedTimeInMillSeconds + " Ms ] ");
return "" + this.elapsedTimeInMillSeconds;
}
public synchronized Long getExecutionTime() {
return elapsedTimeInMillSeconds;
}
}
In your Main class try this :
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class Main {
static final int poolSize = 3;
static int counter = 1;
static final ExecutorService executor = Executors.newFixedThreadPool(poolSize);
public static void main(String[] args) {
List<Callable<Task>> callableTasks = new ArrayList<>();
Callable t1 = new Task("Task1");
Callable t2 = new Task("Task2");
Callable t3 = new Task("Task3");
callableTasks.add(t1);
callableTasks.add(t2);
callableTasks.add(t3);
try {
List<Future<Task>> futures = executor.invokeAll(callableTasks, 3, TimeUnit.SECONDS);
futures.stream().forEach(Ft -> {
Ft.cancel(true);
Task task = null;
try {
task = Ft.get();
} catch (Exception e) {
throw new CancellationException("This Thread has been terminated ");
}
});
executor.shutdownNow();
} catch (Exception e) {
if (e instanceof CancellationException) {
System.out.println("Exception : " + e.getMessage());
}
}
}
}
The Thread where the counter == 2 is going to be terminated, be cause of our delay

What concurrently bug is in this program?

I have one program with strange concurrently bug.
What this program does:
Execute event loop each EVENT_LOOP_PAUSE_DURATION_IN_MS.
For each given task execute processor TaskProcessor
Each 500 ms prints queue size of my executor.
I want to have at most one task in queue per taskId. So, when I add task in queue, I check whether tasks has already existed or not. If there is no task, I add it. In the end of task processing, I remove task from activeTasks map.
If you run the program, then you see the following output:
ERROR: 50
ERROR: 70
ERROR: 80
ERROR: 90
ERROR: 110
ERROR: 120
ERROR: 120
ERROR: 140
So, there is a bug. I don't know why, but size of thread pool queue is infinitely increasing.
You can see, that I remove active tasks in 2 point of program:
In finally block of TaskProcessor, when task has processed.
I remove stale tasks in event loop.
So, if I remove code, which removes tasks at point (2), then the bug disappears. I don't understand this behavior.
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Program {
private static final int NUMBER_OF_TASKS = 40;
private static final int NUMBER_OF_THREADS = 10;
private static final long EVENT_LOOP_PAUSE_DURATION_IN_MS = 40L;
class QueueSizePrinter extends Thread {
private final LinkedBlockingQueue<Runnable> workQueue;
public QueueSizePrinter(LinkedBlockingQueue<Runnable> workQueue) {
this.workQueue = workQueue;
}
#Override
public void run() {
while (true) {
int qSize = workQueue.size();
if (qSize > NUMBER_OF_TASKS) {
System.out.println("ERROR: " + qSize);
}
try {
Thread.sleep(500L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class TaskProcessor implements Runnable {
private final String currentTaskId;
private final ConcurrentHashMap<String, Long> activeTasks;
public TaskProcessor(String currentTaskId, ConcurrentHashMap<String, Long> activeTasks) {
this.currentTaskId = currentTaskId;
this.activeTasks = activeTasks;
}
#Override
public void run() {
try {
// emulate of useful work
Thread.sleep(300L);
} catch (Exception e) {
System.out.println("error: " + e.toString());
} finally {
activeTasks.remove(currentTaskId); // (1)
}
}
}
public void program() {
LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
ExecutorService executor = new ThreadPoolExecutor(NUMBER_OF_THREADS, NUMBER_OF_THREADS, 0L, TimeUnit.MILLISECONDS, workQueue);
Set<String> initialTasks = ConcurrentHashMap.newKeySet();
for (int currentTaskIndex = 0; currentTaskIndex < NUMBER_OF_TASKS; currentTaskIndex++) {
initialTasks.add(String.valueOf(currentTaskIndex));
}
new QueueSizePrinter(workQueue).start();
ConcurrentHashMap<String, Long> activeTasks = new ConcurrentHashMap<>();
while (true) {
initialTasks.forEach((currentTaskId) -> {
if (!activeTasks.containsKey(currentTaskId)) {
activeTasks.put(currentTaskId, System.currentTimeMillis());
executor.submit(new TaskProcessor(currentTaskId, activeTasks));
}
});
// (2)
activeTasks.entrySet().removeIf(entry -> {
boolean hasDelete = System.currentTimeMillis() - entry.getValue() > 1000;
if (hasDelete) {
//System.out.println("DELETE id=" + entry.getKey());
}
return hasDelete;
});
try {
Thread.sleep(EVENT_LOOP_PAUSE_DURATION_IN_MS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Program main = new Program();
main.program();
}
}
Problem is at point (2), You are removing stale tasks from activeTasks map. But they are still submitted to ExecutorService. Since You removed it from the map, when while loop execute another cycle, the same task will be resubmitted to ExecutorService. This causes task numbers to increase.

Can ForkJoinPool be made any faster than ExecutorService in this case?

As long as I give ForkJoinPool one extra thread in the pool it performs equally faster as ExecutorService. Following are the three classes used: Main, RunnableTask and ForkJoinTask. Running on a 16 core box the program outputs following each time:
Executor Time: 5002
ForkJoin Time: 5002
Main Class:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ForkJoinPool;
public class Main {
public static void main(String[] args) throws InterruptedException {
runExecutor(80);
runForkJoin(80);
}
public static void runForkJoin(int size) {
ForkJoinPool fjp = new ForkJoinPool(17);
long start = System.currentTimeMillis();
fjp.invoke(new ForkJoinTask(size));
System.out.println("ForkJoin Time: "
+ (System.currentTimeMillis() - start));
fjp.shutdown();
}
public static void runExecutor(int size) throws InterruptedException {
ExecutorService exec = Executors.newFixedThreadPool(16);
CountDownLatch latch = new CountDownLatch(size);
long start = System.currentTimeMillis();
for (int i = 0; i < latch.getCount(); i++) {
exec.submit(new RunnableTask(latch));
}
latch.await();
System.out.println("Executor Time: "
+ (System.currentTimeMillis() - start));
exec.shutdown();
}
}
Runnable class:
import java.util.concurrent.CountDownLatch;
public class RunnableTask implements Runnable {
private CountDownLatch latch;
public RunnableTask(CountDownLatch latch) {
this.latch = latch;
}
#Override
public void run() {
try {
Thread.sleep(1000);
latch.countDown();
} catch (Exception e) {
}
}
}
RecursiveTask class:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.RecursiveTask;
public class ForkJoinTask extends RecursiveTask {
private List<RecursiveTask> tasks;
private int size;
public ForkJoinTask(int size) {
super();
this.tasks = new ArrayList<>();
this.size = size;
}
#Override
protected Object compute() {
for (int i = 0; i < size; i++) {
RecursiveTask task = new RecursiveTask() {
#Override
protected Object compute() {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
return null;
}
};
task.fork();
tasks.add(task);
}
for (RecursiveTask task : tasks) {
task.join();
}
return null;
}
}
Your individual tasks are such that both ForkJoinPool and an ExecutorService could be made to run faster than they are now, and further that neither one should have a substantial advantage over the other.
The reason why is that if a individual compute task is Thread.sleep(1000) then the task requires no CPU resources. You could up the number of threads to match your job size (80) and complete 80 seconds of 'work' in somewhat more than 1 second elapsed time, because the threads would not really be competing for any kind of resources.
As for the comparison between ForkJoinPool and ExecutorService, the difference isn't relevant to your test case because your bits of work don't result in anything that should be the input to a further calculation (the 'reduce' step in MapReduce). So to you, they're both just thread pools with a different API.

Categories

Resources