I've got an SWT shell that's resizable. Every time it is resized, I have to do something computationally intensive.
I can register a ControlListener on my shell, but this generates events continuously throughout the resize operation, and I have no idea when a resize drag type mouse operation ends.
I'd like to be able to detect when the user is finished resizing the shell and then initiate my computationally intensive operation. Any ideas how to go about that?
How about using a timer and start your operation after a delay of say one sec since last received resize event?
A rough draft:
long lastEvent;
ActionListener taskPerformer = new ActionListener() {
public void doCalc(ActionEvent evt) {
if ( (lastEvent + 1000) < System.currentTimeMillis() ) {
hardcoreCalculationTask();
} else {
// this can be timed better
new Timer(1000, taskPerformer).start();
}
}
};
}
In your resize event:
lastEvent = System.currentTimeMillis();
new Timer(1000, taskPerformer).start();
The solution below was inspired by stacker's and is pretty much the same except that it uses only SWT API and also makes sure the mouse button is up before starting the CPU intensive task.
First the type that does the job:
private class ResizeListener implements ControlListener, Runnable, Listener {
private long lastEvent = 0;
private boolean mouse = true;
public void controlMoved(ControlEvent e) {
}
public void controlResized(ControlEvent e) {
lastEvent = System.currentTimeMillis();
Display.getDefault().timerExec(500, this);
}
public void run() {
if ((lastEvent + 500) < System.currentTimeMillis() && mouse) {
...work
} else {
Display.getDefault().timerExec(500, this);
}
}
public void handleEvent(Event event) {
mouse = event.type == SWT.MouseUp;
}
}
Then we need to register it. Also make sure to unregister when done. One may also want to change the component used for mouse listening in order to be little bit more specific.
ResizeListener listener = new ResizeListener();
widget.addControlListener(listener);
widget.getDisplay().addFilter(SWT.MouseDown, listener);
widget.getDisplay().addFilter(SWT.MouseUp, listener);
Here's an alternative suggestion for the same problem: [platform-swt-dev] Mouse resize listener:
You could try setting a flag and defering the resize work using Display.asyncExec(). When you get a resize, if the flag is set, just return. This should cause the resize work only when the UI is idle.
My instant idea was to listen to mouse up events but obviously (I just tried it), mouse events are not fired for mouse actions on the shell's border. Could be so damn easy...
I solved this problem in a generic way by creating an Executor that can "throttle" tasks.
Tasks (Runnables) are put into a DelayQueue, from where a Scheduler-Thread takes and executes them. The latest scheduled task is also remembered in a variable, so if the Scheduler retrieves a new task from the queue, he checks if this is the latest task that was scheduled. If so, he executes it, if not it's skipped.
I use a String-identifier to check which tasks are considered to belong to one "throttle".
This is the code, it also includes normal scheduling-abilities, but you can check out the essential bits in there.
package org.uilib.util;
import com.google.common.collect.Maps;
import java.util.Map;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class SmartExecutor implements Throttle, Executor {
//~ Static fields/initializers -------------------------------------------------------------------------------------
private static final Logger L = LoggerFactory.getLogger(SmartExecutor.class);
//~ Instance fields ------------------------------------------------------------------------------------------------
private final ExecutorService executor = Executors.newCachedThreadPool();
private final DelayQueue<DelayedRunnable> taskQueue = new DelayQueue<DelayedRunnable>();
private final Map<String, ThrottledRunnable> throttledTasks = Maps.newHashMap();
//~ Constructors ---------------------------------------------------------------------------------------------------
/* schedule a Runnable to be executed a fixed period of time after it was scheduled
* if a new Runnable with the same throttleName is scheduled before this one was called, it will overwrite this */
public SmartExecutor() {
this.executor.execute(new Scheduler());
}
//~ Methods --------------------------------------------------------------------------------------------------------
/* execute a Runnable once */
#Override
public void execute(final Runnable runnable) {
this.executor.execute(runnable);
}
/* schedule a Runnable to be executed after a fixed period of time */
public void schedule(final long delay, final TimeUnit timeUnit, final Runnable runnable) {
this.taskQueue.put(new DelayedRunnable(runnable, delay, timeUnit));
}
/* schedule a Runnable to be executed using a fixed delay between the end of a run and the start of the next one */
public void scheduleAtFixedRate(final long period, final TimeUnit timeUnit, final Runnable runnable) {
this.taskQueue.put(new RepeatingRunnable(runnable, period, timeUnit));
}
/* shut the the executor down */
public void shutdown() {
this.executor.shutdownNow();
}
#Override
public void throttle(final String throttleName, final long delay, final TimeUnit timeUnit, final Runnable runnable) {
final ThrottledRunnable thrRunnable = new ThrottledRunnable(runnable, throttleName, delay, timeUnit);
this.throttledTasks.put(throttleName, thrRunnable);
this.taskQueue.put(thrRunnable);
}
//~ Inner Classes --------------------------------------------------------------------------------------------------
private static class DelayedRunnable implements Delayed, Runnable {
protected final Runnable runnable;
private final long endOfDelay;
public DelayedRunnable(final Runnable runnable, final long delay, final TimeUnit delayUnit) {
this.runnable = runnable;
this.endOfDelay = delayUnit.toMillis(delay) + System.currentTimeMillis();
}
#Override
public int compareTo(final Delayed other) {
final Long delay1 = this.getDelay(TimeUnit.MILLISECONDS);
final Long delay2 = other.getDelay(TimeUnit.MILLISECONDS);
return delay1.compareTo(delay2);
}
#Override
public long getDelay(final TimeUnit unit) {
return unit.convert(this.endOfDelay - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
#Override
public void run() {
this.runnable.run();
}
}
private static final class RepeatingRunnable extends DelayedRunnable {
private final long periodInMillis;
public RepeatingRunnable(final Runnable runnable, final long period, final TimeUnit delayUnit) {
super(runnable, period, delayUnit);
this.periodInMillis = delayUnit.convert(period, TimeUnit.MILLISECONDS);
}
public RepeatingRunnable reschedule() {
return new RepeatingRunnable(this.runnable, this.periodInMillis, TimeUnit.MILLISECONDS);
}
}
private final class Scheduler implements Runnable {
#Override
public void run() {
while (true) {
try {
/* wait for the next runnable to become available */
final DelayedRunnable task = SmartExecutor.this.taskQueue.take();
if (task instanceof RepeatingRunnable) {
/* tell executor to run the action and reschedule it afterwards */
SmartExecutor.this.executor.execute(
new Runnable() {
#Override
public void run() {
task.run();
SmartExecutor.this.taskQueue.put(((RepeatingRunnable) task).reschedule());
}
});
} else if (task instanceof ThrottledRunnable) {
final ThrottledRunnable thrTask = (ThrottledRunnable) task;
/* run only if this is the latest task in given throttle, otherwise skip execution */
if (SmartExecutor.this.throttledTasks.get(thrTask.getThrottleName()) == thrTask) {
SmartExecutor.this.executor.execute(task);
}
} else {
/* tell the executor to just run the action */
SmartExecutor.this.executor.execute(task);
}
} catch (final InterruptedException e) {
SmartExecutor.L.debug("scheduler interrupted (shutting down)");
return;
}
}
}
}
private static final class ThrottledRunnable extends DelayedRunnable {
private final String throttleName;
public ThrottledRunnable(final Runnable runnable, final String throttleName, final long period,
final TimeUnit delayUnit) {
super(runnable, period, delayUnit);
this.throttleName = throttleName;
}
public String getThrottleName() {
return this.throttleName;
}
}
}
If the problem is blocking the UI Thread during resizing, you should consider the method asyncExec of the class Display
/**
* Causes the <code>run()</code> method of the runnable to
* be invoked by the user-interface thread at the next
* reasonable opportunity. The caller of this method continues
* to run in parallel, and is not notified when the
* runnable has completed. Specifying <code>null</code> as the
* runnable simply wakes the user-interface thread when run.
* <p>
* Note that at the time the runnable is invoked, widgets
* that have the receiver as their display may have been
* disposed. Therefore, it is necessary to check for this
* case inside the runnable before accessing the widget.
* </p>
*
* #param runnable code to run on the user-interface thread or <code>null</code>
*
* #exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* #see #syncExec
*/
public void asyncExec (Runnable runnable) {
synchronized (Device.class) {
if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED);
synchronizer.asyncExec (runnable);
}
}
Related
When any command scheduled with fixed rate at any ScheduledExecutorService, it returns ScheduledFuture which can be cancelled as well.
But "cancel" does not provide guarantee that command is not still executing after cancel returns, for example because command was already in the middle of execution when "cancell" was called.
For mostly use cases it is enough functionality. But I have deal with usecase when need to block current thread after cancel, if command already is in progress, and wait until command done. In other words thread which called cancel should not go forward if command still executing. Cancelling with mayInterruptIfRunning=true also is not suitable, because I do not want to broke current executions, I just need to wait for normal complete.
I did not found how to achieve this requirements via standard JDK classes. Question1: Was I wrong and this kind of functionality exists?
So I decided to implement it by itself:
import java.util.concurrent.*;
public class GracefullyStoppingScheduledFutureDecorator implements ScheduledFuture {
/**
* #return the scheduled future with method special implementation of "cancel" method,
* which in additional to standard implementation,
* provides strongly guarantee that command is not in the middle of progress when "cancel" returns
*/
public static ScheduledFuture schedule(Runnable command, long initialDelay, long period, TimeUnit unit, ScheduledExecutorService scheduler) {
CancellableCommand cancellableCommand = new CancellableCommand(command);
ScheduledFuture future = scheduler.scheduleAtFixedRate(cancellableCommand, initialDelay, period, unit);
return new GracefullyStoppingScheduledFutureDecorator(future, cancellableCommand);
}
private GracefullyStoppingScheduledFutureDecorator(ScheduledFuture targetFuture, CancellableCommand command) {
this.targetFuture = targetFuture;
this.runnable = command;
}
private final ScheduledFuture targetFuture;
private final CancellableCommand runnable;
#Override
public boolean cancel(boolean mayInterruptIfRunning) {
runnable.cancel();
return targetFuture.cancel(mayInterruptIfRunning);
}
#Override
public long getDelay(TimeUnit unit) {
return targetFuture.getDelay(unit);
}
#Override
public int compareTo(Delayed o) {
return targetFuture.compareTo(o);
}
#Override
public boolean isCancelled() {
return targetFuture.isCancelled();
}
#Override
public boolean isDone() {
return targetFuture.isDone();
}
#Override
public Object get() throws InterruptedException, ExecutionException {
return targetFuture.get();
}
#Override
public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return targetFuture.get(timeout, unit);
}
private static class CancellableCommand implements Runnable {
private final Object monitor = new Object();
private final Runnable target;
private boolean cancelled = false;
private CancellableCommand(Runnable target) {
this.target = target;
}
public void cancel() {
synchronized (monitor) {
cancelled = true;
}
}
#Override
public void run() {
synchronized (monitor) {
if (!cancelled) {
target.run();
}
}
}
}
}
Question2: Could anybody find errors in the code above?
Question2: Could anybody find errors in the code above?
There is hypothetical deadlock which can be reproduced by following scenario:
Having thread T1 which holds monitor M1
Scheduled task is executing(holds its monitor M2) on thread T2 and wants to enter to M1, so T2 need to wait until T1 exits monitor M1.
T1 decided to cancel task, but because its monitor M2 is locked by task itself we have the deadlock.
Most likely scenario abovr is unreal, but to protect from all possible cases, I decided to rewrite code in lock-free manner:
public class GracefullyStoppingScheduledFuture {
/**
* #return the scheduled future with method special implementation of "cancel" method,
* which in additional to standard implementation,
* provides strongly guarantee that command is not in the middle of progress when "cancel" returns
*/
public static GracefullyStoppingScheduledFuture cheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit, ScheduledExecutorService scheduler) {
CancellableCommand cancellableCommand = new CancellableCommand(command);
ScheduledFuture future = scheduler.scheduleAtFixedRate(cancellableCommand, initialDelay, period, unit);
return new GracefullyStoppingScheduledFuture(future, cancellableCommand);
}
private GracefullyStoppingScheduledFuture(ScheduledFuture targetFuture, CancellableCommand command) {
this.targetFuture = targetFuture;
this.runnable = command;
}
private final ScheduledFuture targetFuture;
private final CancellableCommand runnable;
public void cancelAndBeSureOfTermination(boolean mayInterruptIfRunning) throws InterruptedException, ExecutionException {
try {
targetFuture.cancel(mayInterruptIfRunning);
} finally {
runnable.cancel();
}
}
private static class CancellableCommand implements Runnable {
private static final int NOT_EXECUTING = 0;
private static final int IN_PROGRESS = 1;
private static final int CANCELLED_WITHOUT_OBSTRUCTION = 2;
private static final int CANCELLED_IN_MIDDLE_OF_PROGRESS = 3;
private final AtomicInteger state = new AtomicInteger(NOT_EXECUTING);
private final AtomicReference<Thread> executionThread = new AtomicReference<>();
private final CompletableFuture<Void> cancellationFuture = new CompletableFuture<>();
private final Runnable target;
private CancellableCommand(Runnable target) {
this.target = target;
}
public void cancel() throws ExecutionException, InterruptedException {
if (executionThread.get() == Thread.currentThread()) {
// cancel method was called from target by itself
state.set(CANCELLED_IN_MIDDLE_OF_PROGRESS);
return;
}
while (true) {
if (state.get() == CANCELLED_WITHOUT_OBSTRUCTION) {
return;
}
if (state.get() == CANCELLED_IN_MIDDLE_OF_PROGRESS) {
cancellationFuture.get();
return;
}
if (state.compareAndSet(NOT_EXECUTING, CANCELLED_WITHOUT_OBSTRUCTION)) {
return;
}
if (state.compareAndSet(IN_PROGRESS, CANCELLED_IN_MIDDLE_OF_PROGRESS)) {
cancellationFuture.get();
return;
}
}
}
#Override
public void run() {
if (!state.compareAndSet(NOT_EXECUTING, IN_PROGRESS)) {
notifyWaiters();
return;
}
try {
executionThread.set(Thread.currentThread());
target.run();
} finally {
executionThread.set(null);
if (!state.compareAndSet(IN_PROGRESS, NOT_EXECUTING)) {
notifyWaiters();
}
}
}
private void notifyWaiters() {
if (state.get() == CANCELLED_WITHOUT_OBSTRUCTION) {
// no need to notify anything
return;
}
// someone waits for cancelling
cancellationFuture.complete(null);
return;
}
}
I have a game where I am scheduling a timer. I have this CoresManager file:
package com.rs.cores;
import java.util.Timer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
public final class CoresManager {
protected static volatile boolean shutdown;
public static WorldThread worldThread;
public static ExecutorService serverWorkerChannelExecutor;
public static ExecutorService serverBossChannelExecutor;
public static Timer fastExecutor;
public static ScheduledExecutorService slowExecutor;
public static int serverWorkersCount;
public static void init() {
worldThread = new WorldThread();
int availableProcessors = Runtime.getRuntime().availableProcessors();
serverWorkersCount = availableProcessors >= 6 ? availableProcessors - (availableProcessors >= 12 ? 7 : 5) : 1;
serverWorkerChannelExecutor = availableProcessors >= 6 ? Executors
.newFixedThreadPool(availableProcessors - (availableProcessors >= 12 ? 7 : 5),
new DecoderThreadFactory()) : Executors.newSingleThreadExecutor(new DecoderThreadFactory());
serverBossChannelExecutor = Executors
.newSingleThreadExecutor(new DecoderThreadFactory());
fastExecutor = new Timer("Fast Executor");
slowExecutor = availableProcessors >= 6 ? Executors.newScheduledThreadPool(availableProcessors >= 12 ? 4 : 2,
new SlowThreadFactory()) : Executors
.newSingleThreadScheduledExecutor(new SlowThreadFactory());
worldThread.start();
}
public static void shutdown() {
serverWorkerChannelExecutor.shutdown();
serverBossChannelExecutor.shutdown();
fastExecutor.cancel();
slowExecutor.shutdown();
shutdown = true;
}
private CoresManager() {
}
}
I am using this inside the game:
private void startTimer() {
CoresManager.fastExecutor.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
if (timer == 0 || timer < 1) {
player.sm("Your timer has ended! The NPCs will no longer spawn.");
timer = 0;
this.cancel();
exitInstance();
return;
}
timer--;
timerchecker = true;
seconds = timer % 60;
player.setTimer(timer);
minutes = TimeUnit.SECONDS.toMinutes(timer);
}
}, 0, 1000);
}
The CoresManager Timer stops running if the player logs out AND the server gets rebooted. To make it run again, I added a code to make it do startTimer() again once you log back in. However, since the timer still runs if the server didn't log out, the timer starts running twice. The Timer starts getting subtracted by 2, or more, depending on how many times you log out and in. I figure that it would fix if there was a code to determine if the timer is already running. Is there a way to do this? Please help!
I don't see anything in the documentation that provides for checking the status on a TimerTask object (http://docs.oracle.com/javase/1.5.0/docs/api/java/util/TimerTask.html) so one option would be to extend TimerTask and create your own class. Instead of using an anonymous TimerTask, you could create something along the lines of:
public class CoresTimerTask extends TimerTask {
private boolean hasStarted = false;
#Overrides
public void run() {
this.hasStarted = true;
//rest of run logic here...
}
public boolean hasRunStarted() {
return this.hasStarted;
}
}
and just maintain a reference to this CoresTimerTask object, which you then pass into startTimer(). You can then later check this object via hasRunStarted.
public long scheduledExecutionTime()
Returns the scheduled execution time of the most recent actual execution of this task. (If this method is invoked while task execution is in progress, the return value is the scheduled execution time of the ongoing task The return value is undefined if the task has yet to commence its first execution.
This method is typically not used in conjunction with fixed-delay execution repeating tasks, as their scheduled execution times are allowed to drift over time, and so are not terribly significant.
first thing periodically running tasks need set/reset state flag
second (when i look at examples) it is better to seal this type of class
but if someone insist to have such methods
public abstract class NonInterruptableTask extends TimerTask {
protected boolean isDone = false;
public boolean isDone() {return isDone;}
protected abstract void doTaskWork();
#Override
public void run() {
isDone = false;
doTaskWork();
isDone = true;
}
}
usage:
TimerTask myTask = new NonInterruptableTask() {
#Override
public void doTaskWork() {
//job here
}
};
you could also declare a boolean state called like "timerstate" or whatever and make it by default to be false. whenever you start a timer you could change this boolean to true and you'd be able to keep track of the timer.
public boolean timerstate;
public Timer t1;
// some code goes here to do whatever you want
if(timerstate == true) {
t1.cancel();
t1.purge();
t1 = new Timer();
} else{
t1.schedule(new TimerTask() {
#Override
public void run() {
timerstate = true;
//rest of code for the timer goes here
}
}
}
How to set the Thread priority of a Timer in java? This is the code I have found in the project that I am working on, and I do not think that it is working:
public static Timer createNamedTimer(boolean isDaemon,
final String threadName, final int priority) {
Timer timer = new Timer(isDaemon);
timer.schedule(new TimerTask() {
public void run() {
Thread.currentThread().setName("TimerThread: " + threadName);
Thread.currentThread().setPriority(priority);
}
}, 0);
return timer;
}
AFAIK for timer the only way you can change priority is the way you are doing it.
If you need a better option you can use the ThreadFactory for creating the threads and setting their priority.
class SimpleThreadFactory implements ThreadFactory {
private int threadPriority;
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setPriority(threadPriority);
return t;
}
}
Then you can pass the factory to the Executors framework of Java for doing what you want, IMHO this will be a much better approach.
Why do I say it would be a better approach?
The Timer class's JavaDoc mentions ScheduledThreadPoolExecutor and notes, that this class is effectively a more versatile replacement for the Timer/TimerTask combination
The suggested solution won't likely work for tasks that are repeated more than once, because between invocations another task that shared the same thread may have adjusted the priority to something else. Therefore, for repeating tasks you must set the priority at execution time, every time. This potential issue exists w/or w/o the new Executors framework.
One solution is to create a wrapper class that does prep work for you to ensure consistency. For example:
AnyClass.java:
private static void exampleUsage()
{
try { launchHighPriorityTask(() -> System.out.println("What a fancy task.")).join(); }
catch (Throwable ignored) {}
}
private static Thread launchMaxPriorityTask(Runnable task)
{
final Thread customThread = new Thread(new Task("MaxPriority", Thread.MAX_PRIORITY, task));
customThread.start();
return customThread;
}
Task.java:
public class Task implements Runnable
{
private final String name;
private final int priority;
private final Runnable task;
public Task(String name, int priority, Runnable task)
{
if (null == task) throw new NullPointerException("no task provided");
this.name = name; this.priority = priority; this.task = task;
}
/**
* run() is made final here to prevent any deriving classes
* accidentally ruining the expected behavior
*/
#Override public final void run()
{
final Thread thread = Thread.currentThread();
// cache the current state to restore settings and be polite
final String prevName = thread.getName();
final int prevPriority = thread.getPriority();
// set our thread's config
thread.setName(name);
thread.setPriority(priority);
try { task.run(); } catch (Throwable ignored) {}
// restore previous thread config
thread.setPriority(prevPriority);
thread.setName(prevName);
}
}
This is naturally a minimalist example of what can be accomplished with this sort of setup.
Quick note: Java and Android noob here, I'm open to you telling me I'm stupid (as long as you tell me why.)
I have an android application which requires me start multiple threads originating from various classes and only advance to the next activity once all threads have done their job. I also want to add a "failsafe" timeout in case one the the threads takes too long (HTTP request taking too long or something.)
I searched Stack Overflow and found a post saying that I should create a class to keep a running total of open threads and then use a timer to poll for when all the threads are completed.
I think I've created a working class to do this for me, it's untested as of yet but has no errors showing in eclipse.
Is this a correct implementation? Are there any APIs that I should be made aware of (such as classes in the Java or Android APIs that could be used in place of the abstract classes at the bottom of the class?)
package com.dmp.geofix.libs;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
public class ThreadMonitor {
private Timer timer = null;
private TimerTask timerTask = null;
private OnSuccess onSuccess = null;
private OnError onError = null;
private static ArrayList<Thread> threads;
private final int POLL_OPEN_THREADS = 100;
private final int TIMEOUT = 10000;
public ThreadMonitor() {
timerTask = new PollThreadsTask();
}
public ThreadMonitor(OnSuccess s) {
timerTask = new PollThreadsTask();
onSuccess = s;
}
public ThreadMonitor(OnError e) {
timerTask = new PollThreadsTask();
onError = e;
}
public ThreadMonitor(OnSuccess s, OnError e) {
timerTask = new PollThreadsTask();
onSuccess = s;
onError = e;
}
public void start() {
Iterator<Thread> i = threads.iterator();
while (i.hasNext()) {
i.next().start();
}
timer = new Timer();
timer.schedule(timerTask, 0, POLL_OPEN_THREADS);
}
public void finish() {
Iterator<Thread> i = threads.iterator();
while (i.hasNext()) {
i.next().interrupt();
}
threads.clear();
timer.cancel();
}
public void addThread(Thread t) {
threads.add(t);
}
public void removeThread(Thread t) {
threads.remove(t);
t.interrupt();
}
class PollThreadsTask extends TimerTask {
private int timeElapsed = 0;
#Override
public void run() {
timeElapsed += POLL_OPEN_THREADS;
if (timeElapsed <= TIMEOUT) {
if (threads.isEmpty() == false) {
if (onSuccess != null) {
onSuccess.run();
}
}
} else {
if (onError != null) {
onError.run();
}
finish();
}
}
}
public abstract class OnSuccess {
public abstract void run();
}
public abstract class OnError {
public abstract void run();
}
}
Take a look at Thread.join
Check java.util.concurrent package .
specifically Future, FutureTask and ExecutorService
FutureTask allows to get the status of the operation under execution with methid isDone()
I hope this helps
All the threads should be implementing callable and they should be future tasks.
Start all of them with countdownlatch/CyclicBarrier to ensure all of them start at same time
If you intend for the application to exit after running the code, you might want to have a look at Runtime.getRuntime().addShutdownHook(Thread) to specify a thread to be executed when the application is exiting.
So, I'm curious. How do you handle setting maximum execution time for threads? When running in a thread pool?
I have several techniques but, I'm never quite satisfied with them. So, I figure I'd ask the community how they go about it.
How about:
Submit your Callable to the ExecutorService and keep a handle to the returned Future.
ExecutorService executorService = ... // Create ExecutorService.
Callable<Result> callable = new MyCallable(); // Create work to be done.
Future<Result> fut = executorService.submit(callable);
Wrap the Future in an implementation of Delayed whereby Delayed's getDelay(TimeUnit) method returns the maximum execution time for the work in question.
public class DelayedImpl<T> implements Delayed {
private final long maxExecTimeMillis;
private final Future<T> future;
public DelayedImpl(long maxExecTimeMillis, Future<T> future) {
this.maxExecMillis = maxExecMillis;
this.future = future;
}
public TimeUnit getDelay(TimeUnit timeUnit) {
return timeUnit.convert(maxExecTimeMillis, TimeUnit.MILLISECONDS);
}
public Future<T> getFuture() {
return future;
}
}
DelayedImpl impl = new DelayedImpl(3000L, fut); // Max exec. time == 3000ms.
Add the `DelayedImpl` to a `DelayQueue`.
Queue<DelayedImpl> queue = new DelayQueue<DelayImpl>();
queue.add(impl);
Have a thread repeatedly take() from the queue and check whether each DelayedImpl's Future is complete by calling isDone(); If not then cancel the task.
new Thread(new Runnable() {
public void run() {
while (!Thread.interrupted) {
DelayedImpl impl = queue.take(); // Perform blocking take.
if (!impl.getFuture().isDone()) {
impl.getFuture().cancel(true);
}
}
}
}).start();
The main advantage to this approach is that you can set a different maximum execution time per task and the delay queue will automatically return the task with the smallest amount of execution time remaining.
Normally, I just poll regularly a control object from the threaded code. Something like:
interface ThreadControl {
boolean shouldContinue();
}
class Timer implements ThreadControl {
public boolean shouldContinue() {
// returns false if max_time has elapsed
}
}
class MyTask implements Runnable {
private tc;
public MyTask(ThreadControl tc) {
this.tc = tc;
}
public void run() {
while (true) {
// do stuff
if (!tc.shouldContinue())
break;
}
}
}
Adamski:
I believe that your implementation of the Delayed Interface requires some adjustment in order to work properly. The return value of 'getDelay()' should return a negative value if the amount of time elapsed from the instantiation of the object has exceeded the maximum lifetime. To achieve that, you need to store the time when the task was created (and presumably started). Then each time 'getDelay()' is invoked, calculate whether or not the maximum lifetime of the thread has been exceeded. As in:
class DelayedImpl<T> implements Delayed {
private Future<T> task;
private final long maxExecTimeMinutes = MAX_THREAD_LIFE_MINUTES;
private final long startInMillis = System.currentTimeMillis();
private DelayedImpl(Future<T> task) {
this.task = task;
}
public long getDelay(TimeUnit unit) {
return unit.convert((startInMillis + maxExecTimeMinutes*60*1000) - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
public int compareTo(Delayed o) {
Long thisDelay = getDelay(TimeUnit.MILLISECONDS);
Long thatDelay = o.getDelay(TimeUnit.MILLISECONDS);
return thisDelay.compareTo(thatDelay);
}
public Future<T> getTask() {
return task;
}
}