如何进入FutureTask执行状态?
我有一个singleThreadExecutor,以执行我按顺序提交给它的任务,即一个接一个的任务,没有并行执行。
我有runnable这样的东西
MyRunnable implements Runnable { @Override public void run() { try { Thread.sleep(30000); } catch (InterruptedException e1) { e1.printStackTrace(); }
}
例如,当我向上述单线程执行程序提交三个MyRunnable实例时,我希望第一个任务执行,因为Thread.sleep在TIMED_WAITING中有执行线程(我可能错误的具体州)。 其他两个任务不应该分配线程来执行它们,至少在第一个任务完成之前不会。
所以我的问题是如何通过FutureTask API获取此状态或以某种方式到达正在执行任务的线程(如果没有这样的线程然后任务等待执行或挂起)并获得其状态或者可能由某些其他方式?
FutureTask只定义了isCanceled()和isDone()方法,但这些方法还不足以描述Task的所有可能的执行状态。
您可以将一个getThread()
方法添加到MyRunnable
,以生成执行run()
方法的Thread
。
我建议添加一个这样的实例变量(必须是volatile才能确保正确性):
private volatile Thread myThread;
在try
块之前执行此操作:
myThread = Thread.currentThread();
并添加一个finally
块:
myThread = null;
然后你可以打电话:
final Thread theThread = myRunnable.getThread(); if (theThread != null) { System.out.println(theThread.getState()); }
对于一些MyRunnable
。
null
在这一点上是一个模棱两可的结果,意思是“没有运行”或“已经完成”。 只需添加一个方法,告诉操作是否已完成:
public boolean isDone() { return done; }
当然,您需要一个实例变量来记录这种状态:
private volatile boolean done;
并且在finally
块中将其设置为true(可能在将线程设置为null
,那里存在一些竞争条件,因为有两个值捕获一个事物的状态。特别是,使用这种方法,您可以观察到isDone() == true
和getThread() != null
。您可以通过为状态转换设置lock
对象并在更改一个或两个状态变量时对其进行同步来缓解这种情况:
done = true;
请注意,仍然没有任何防护措施禁止单个MyRunnable
同时提交给两个或多个线程。 我知道你说你今天没有这样做……今天多个并发执行会导致状态损坏,可能性很高。 您可以在run()
方法的开头放置一些互斥保护(例如在run()
方法上简单地写synchronized
),以确保在任何给定时间只发生一次执行。
您可以在Runnable
中包装您提交给此服务的任何内容,该Runnable
在输入其run方法时进行记录。
public class RecordingRunnable implements Runnable { private final Runnable actualTask; private volatile boolean isRunning = false; //constructor, etc public void run() { isRunning = true; actualTask.run(); isRunning = false; } public boolean isRunning() { return isRunning; } }
如果您想要非常彻底, FutureTask
会在内部跟踪状态READY
, RUNNING
, RAN
和CANCELLED
。 您可以创建此类的副本并为该状态添加访问器。 然后重写AbstractExecutorService.newTaskFor(Runnable)
以使用CustomFutureTask
包装它(内部类是private
,所以只是子类化不起作用)。
newTaskFor(Runnable)
的默认实现非常简单:
protected RunnableFuture newTaskFor(Runnable runnable, T value) { return new FutureTask (runnable, value); }
所以覆盖它并不是什么大不了的事。
由于FutureTask需要一个可调用的对象,我们将创建一个简单的Callable实现。
import java.util.concurrent.Callable; public class MyCallable implements Callable { private long waitTime; public MyCallable(int timeInMillis){ this.waitTime=timeInMillis; } @Override public String call() throws Exception { Thread.sleep(waitTime); //return the thread name executing this callable task return Thread.currentThread().getName(); } }
下面是FutureTask方法的一个示例,它显示了FutureTask的常用方法。
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class FutureTaskExample { public static void main(String[] args) { MyCallable callable1 = new MyCallable(1000); MyCallable callable2 = new MyCallable(2000); FutureTask futureTask1 = new FutureTask (callable1); FutureTask futureTask2 = new FutureTask (callable2); ExecutorService executor = Executors.newFixedThreadPool(2); executor.execute(futureTask1); executor.execute(futureTask2); while (true) { try { if(futureTask1.isDone() && futureTask2.isDone()){ System.out.println("Done"); //shut down executor service executor.shutdown(); return; } if(!futureTask1.isDone()){ //wait indefinitely for future task to complete System.out.println("FutureTask1 output="+futureTask1.get()); } System.out.println("Waiting for FutureTask2 to complete"); String s = futureTask2.get(200L, TimeUnit.MILLISECONDS); if(s !=null){ System.out.println("FutureTask2 output="+s); } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }catch(TimeoutException e){ //do nothing } } } }