简体   繁体   English

如何进入FutureTask执行状态?

[英]How to get to FutureTask execution state?

I have a singleThreadExecutor in order to execute the tasks I submit to it in serial order ie one task after another, no parallel execution. 我有一个singleThreadExecutor以便以串行顺序执行提交给它的任务,即一个任务接一个,没有并行执行。

I have runnable which goes something like this 我有可运行的东西,像这样

MyRunnable implements Runnable {

@Override
public void run() {
    try {
        Thread.sleep(30000);
    } catch (InterruptedException e1) {
        e1.printStackTrace();
    }

} }

When I submit ,for example, three instances of MyRunnable to the afore-mentioned single thread executor, I would expect to have the first task executing and because of the Thread.sleep has Its executing thread in TIMED_WAITING (I may be wrong about the specific state). 例如,当我向上述单线程执行器提交MyRunnable的三个实例时,我希望执行第一个任务,并且由于Thread.sleep在TIMED_WAITING中具有其执行线程(我可能对特定的线程有误州)。 The other two tasks should not have threads assigned to execute them, at least not until the first task has finished. 其他两个任务不应分配线程来执行它们,至少直到第一个任务完成时才分配线程。

So my question is how to get this state via the FutureTask API or somehow get to the thread that is executing the task (if there is no such thread then the task is waiting to be executed or pending) and get Its state or perhaps by some other means? 所以我的问题是如何通过FutureTask API来获得此状态,或者以某种方式到达正在执行任务的线程(如果没有这样的线程,则该任务正等待执行或挂起)并获得其状态,或者通过某种方式其他手段?

FutureTask only defines isCanceled() and isDone() methods, but those are not quite enough to describe all the possible execution statuses of the Task. FutureTask仅定义isCanceled()和isDone()方法,但是这些方法不足以描述Task的所有可能执行状态。

You could wrap anything you submit to this service in a Runnable that records when its run method is entered. 您可以将提交给该服务的所有内容包装到一个Runnable ,该Runnable记录输入其运行方法的时间。

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;
    }
}

You could add a getThread() method to MyRunnable that produces the Thread executing the run() method. 您可以在MyRunnable中添加一个getThread()方法,该方法将生成执行run()方法的Thread

I would suggest adding an instance variable like this (must be volatile to ensure correctness): 我建议添加一个这样的实例变量(必须可变以确保正确性):

 private volatile Thread myThread;

Do this before the try block: try块之前执行此操作:

myThread = Thread.currentThread();

And add a finally block with this: 并添加一个finally块:

myThread = null;

Then you could call: 然后您可以致电:

final Thread theThread = myRunnable.getThread();
if (theThread != null) {
    System.out.println(theThread.getState());
}

for some MyRunnable . 对于一些MyRunnable

null is an ambiguous result at this point, meaning either, "hasn't run," or "has completed." null是模棱两可的结果,表示“未运行”或“已完成”。 Simply add a method that tells whether the operation has completed: 只需添加一个方法来告知操作是否已完成:

public boolean isDone() {
    return done;
}

Of course, you'll need an instance variable to record this state: 当然,您需要一个实例变量来记录此状态:

private volatile boolean done;

And set it to true in the finally block (probably before setting the thread to null , there's a bit of a race condition there because there are two values capturing the state of one thing. In particular, with this approach you could observe isDone() == true and getThread() != null . You could mitigate this by having a lock object for state transitions and synchronize on it when changing one or both state variables): 并在finally块中将其设置为true(可能在将线程设置为null ,那里存在一些竞争条件,因为有两个值捕获了一件事情的状态。特别是,使用这种方法,您可以观察到isDone() == truegetThread() != null ,您可以通过添加一个用于状态转换的lock对象来缓解这种情况,并在更改一个或两个状态变量时对其进行同步):

done = true;

Note that there still isn't any guard that prohibits a single MyRunnable from being submitted concurrently to two or more threads. 请注意,仍然没有任何防护措施禁止将单个MyRunnable同时提交给两个或多个线程。 I know you say that you're not doing this... today :) Multiple concurrent executions will lead to corrupted state with high likelihood. 我知道您说您今天不这样做... :)多个并发执行极有可能导致损坏状态。 You could put some mutual exclusive guard (such as simply writing synchronized on the run() method) at the beginning of the run method to ensure that only a single execution is happening at any given time. 您可以在run()方法的开头放置一些互斥防护(例如,简单地在run()方法上synchronized编写),以确保在任何给定时间仅发生一次执行。

If you wanted to be really thorough, FutureTask keeps track of states READY , RUNNING , RAN , and CANCELLED internally. 如果您想真正做到透彻, FutureTask会在内部跟踪状态READYRUNNINGRANCANCELLED You could create a copy of this class and add an accessor for the state. 您可以创建此类的副本并为该状态添加访问器。 Then override AbstractExecutorService.newTaskFor(Runnable) to wrap it using your CustomFutureTask (the inner class is private , so just subclassing won't work). 然后重写AbstractExecutorService.newTaskFor(Runnable)以使用您的CustomFutureTask对其进行包装(内部类是private ,因此子类将无法工作)。

The default implementation of newTaskFor(Runnable) is really simple: newTaskFor(Runnable)的默认实现非常简单:

protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    return new FutureTask<T>(runnable, value);
}

so it wouldn't be a big deal to override it. 因此覆盖它并没什么大不了的。

Since FutureTask requires a callable object, we will create a simple Callable implementation. 由于FutureTask需要可调用对象,因此我们将创建一个简单的Callable实现。

import java.util.concurrent.Callable;

    public class MyCallable implements Callable<String> {

        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();
        }

    }

Here is an example of FutureTask method and it's showing commonly used methods of FutureTask. 这是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<String> futureTask1 = new FutureTask<String>(callable1);
        FutureTask<String> futureTask2 = new FutureTask<String>(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
            }
        }

    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM