Как попасть в состояние выполнения FutureTask? - PullRequest
5 голосов
/ 04 августа 2011

У меня есть singleThreadExecutor для выполнения задач, которые я ему отправляю, в последовательном порядке, то есть одна задача за другой, параллельного выполнения нет.

У меня есть runnable, который выглядит примерно так

MyRunnable implements Runnable {

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

}

Когда я отправляю, например, три экземпляра MyRunnable вышеупомянутому исполнителю с одним потоком, я ожидаю, что будет выполнено первое задание, и поскольку Thread.sleep имеет свой поток выполнения в TIMED_WAITING (я могу ошибаться о конкретном состоянии). Две другие задачи не должны иметь назначенных потоков для их выполнения, по крайней мере, до тех пор, пока первая задача не будет завершена.

Поэтому мой вопрос заключается в том, как получить это состояние через API FutureTask или каким-либо образом добраться до потока, выполняющего задачу (если такого потока нет, то задача ожидает выполнения или ожидания) и получить свое состояние или возможно каким-то другим способом?

FutureTask определяет только методы isCanceled () и isDone (), но их недостаточно для описания всех возможных состояний выполнения Задачи.

Ответы [ 4 ]

3 голосов
/ 04 августа 2011

Вы можете поместить все, что отправляете в эту службу, в 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;
    }
}
2 голосов
/ 04 августа 2011

Вы можете добавить метод getThread() к MyRunnable, который создает Thread, выполняющий метод run().

Я бы предложил добавить переменную экземпляра следующим образом (для обеспечения правильности 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;

И установите для него значение true в блоке finally (вероятно, до установки потока в null, там есть некоторое состояние гонки, потому что есть два значения, фиксирующие состояние одной вещи. В частности, с этим подход, который вы могли бы наблюдать isDone() == true и getThread() != null. Вы могли бы смягчить это, имея объект lock для переходов состояний и синхронизировать его при изменении одной или обеих переменных состояния):

done = true;

Обратите внимание, что до сих пор нет охранника, который запрещает одновременную отправку одного MyRunnable двум или более потокам. Я знаю, вы говорите, что вы не делаете этого ... сегодня :) Многократные одновременные выполнения приведут к поврежденному состоянию с высокой вероятностью. Вы могли бы установить некоторую взаимную исключительную защиту (например, просто написав synchronized в методе run()) в начале метода run, чтобы гарантировать, что в любой момент времени выполняется только одно выполнение.

1 голос
/ 15 сентября 2012

Если вы хотите быть действительно тщательным, FutureTask отслеживает состояния READY, RUNNING, RAN и CANCELLED внутри.Вы можете создать копию этого класса и добавить средство доступа к состоянию.Затем переопределите AbstractExecutorService.newTaskFor(Runnable), чтобы обернуть его, используя CustomFutureTask (внутренний класс - private, так что просто подклассы не сработают).

Реализация по умолчанию newTaskFor(Runnable) действительно проста:

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

так что было бы не сложно переопределить его.

0 голосов
/ 25 июля 2014

Поскольку 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();
        }

    }

Вот пример метода 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
            }
        }

    }
}
...