Java — определите тайм-аут для Callable в ExecutorCompletionService

У меня возникла следующая проблема с использованием ExecutorCompletionService. Я хочу вызвать много Callable в разных потоках. Эти Callable не делятся никакой информацией друг с другом. Мне нужно определить тайм-аут для каждого Callable, например. не бегайте дольше 5 секунд. Каждый Callable может работать в разное время, которое я не знаю при запуске. По истечении тайм-аута поток должен быть остановлен/убит, и результат меня больше не интересует. Другие «нормальные» запущенные потоки не должны подвергаться влиянию.

Итак, давайте возьмем следующий пример с простым вызываемым объектом и моим текущим кодом Java.

import java.util.Date;
import java.util.concurrent.Callable;

public class Job implements Callable<Integer> {

    int returnValue = 0;
    long millis = 0;

    public Job(long millis, int value) {
        this.millis = millis;
        this.returnValue = value;
    }

    @Override
    public Integer call() throws Exception, InterruptedException {
        try {
            System.out.println(new Date() + " " + returnValue + " started");
            Thread.sleep(millis);
            System.out.println(new Date() + " " + returnValue + " finished");
            return returnValue;
        } catch (InterruptedException e) {
            System.out.println(new Date() + " " + returnValue + " interrupted");
            throw e;
        }        
    }
}

И другой класс, где используется Callable.

import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.*;

public class CallableTest {

    public static void main(String[] args) {
        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);
        CompletionService<Integer> pool = new ExecutorCompletionService<Integer>(newFixedThreadPool);
        
        for (int i = 10; i > 0; i--) {
            Job job = new Job(i * 1000, i);
            pool.submit(job);
        }
        
        ArrayList<Integer> results = new ArrayList<Integer>();
        for (int i = 1; i < 11; ++i) {
            try {
                Future<Integer> future = pool.take();
                Integer content = future.get(5, TimeUnit.SECONDS);
                results.add(content);
                System.out.println(new Date() + " added " + content);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        newFixedThreadPool.shutdownNow();

        System.out.println(new Date() + " results:");
        for (int j : results) {
            System.out.println(new Date() + " " + j);
        }
    }
}

Вывод примерно такой:

Sun Jun 29 08:01:00 CEST 2014 10 started
Sun Jun 29 08:01:00 CEST 2014 9 started
Sun Jun 29 08:01:09 CEST 2014 9 finished
Sun Jun 29 08:01:09 CEST 2014 added 9
Sun Jun 29 08:01:09 CEST 2014 8 started
Sun Jun 29 08:01:10 CEST 2014 10 finished
Sun Jun 29 08:01:10 CEST 2014 7 started
Sun Jun 29 08:01:10 CEST 2014 added 10
Sun Jun 29 08:01:17 CEST 2014 7 finished
Sun Jun 29 08:01:17 CEST 2014 6 started
Sun Jun 29 08:01:17 CEST 2014 added 7
Sun Jun 29 08:01:17 CEST 2014 8 finished
Sun Jun 29 08:01:17 CEST 2014 added 8
Sun Jun 29 08:01:17 CEST 2014 5 started
Sun Jun 29 08:01:22 CEST 2014 5 finished
Sun Jun 29 08:01:22 CEST 2014 added 5
Sun Jun 29 08:01:22 CEST 2014 4 started
Sun Jun 29 08:01:23 CEST 2014 6 finished
Sun Jun 29 08:01:23 CEST 2014 3 started
Sun Jun 29 08:01:23 CEST 2014 added 6
Sun Jun 29 08:01:26 CEST 2014 3 finished
Sun Jun 29 08:01:26 CEST 2014 2 started
Sun Jun 29 08:01:26 CEST 2014 added 3
Sun Jun 29 08:01:26 CEST 2014 4 finished
Sun Jun 29 08:01:26 CEST 2014 1 started
Sun Jun 29 08:01:26 CEST 2014 added 4
Sun Jun 29 08:01:27 CEST 2014 1 finished
Sun Jun 29 08:01:27 CEST 2014 added 1
Sun Jun 29 08:01:28 CEST 2014 2 finished
Sun Jun 29 08:01:28 CEST 2014 added 2
Sun Jun 29 08:01:28 CEST 2014 results:
Sun Jun 29 08:01:28 CEST 2014 9
Sun Jun 29 08:01:28 CEST 2014 10
Sun Jun 29 08:01:28 CEST 2014 7
Sun Jun 29 08:01:28 CEST 2014 8
Sun Jun 29 08:01:28 CEST 2014 5
Sun Jun 29 08:01:28 CEST 2014 6
Sun Jun 29 08:01:28 CEST 2014 3
Sun Jun 29 08:01:28 CEST 2014 4
Sun Jun 29 08:01:28 CEST 2014 1
Sun Jun 29 08:01:28 CEST 2014 2 

Это не работает так, как хотелось бы. Я хочу, чтобы каждый Callable, работающий дольше 5 секунд, был прекращен/закончен/прерван, и только Callable, работающий менее 5 секунд, давал мне действительный результат.

Я также пробовал без ExecutorCompletionService.

public class CallableTest2 {
    public static void main(String[] args) {
        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);
        List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
        
        for (int i = 10; i > 0; i--) {
            Job job = new Job(i * 1000, i);
            futures.add(newFixedThreadPool.submit(job));
        }
        
        ArrayList<Integer> results = new ArrayList<Integer>();
        for (Future<Integer> future: futures) {
            try {
                Integer content = future.get(5, TimeUnit.SECONDS);
                results.add(content);
                System.out.println(new Date() + " added " + content);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        newFixedThreadPool.shutdownNow();

        System.out.println(new Date() + " results:");
        for (int j : results) {
            System.out.println(new Date() + " " + j);
        }
    }
}

С результатами:

Sun Jun 29 08:33:19 CEST 2014 9 started
Sun Jun 29 08:33:19 CEST 2014 10 started
java.util.concurrent.TimeoutException
    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:228)
    at java.util.concurrent.FutureTask.get(FutureTask.java:91)
    at callabletest.CallableTest2.main(CallableTest2.java:29)
Sun Jun 29 08:33:28 CEST 2014 9 finished
Sun Jun 29 08:33:28 CEST 2014 8 started
Sun Jun 29 08:33:28 CEST 2014 added 9
Sun Jun 29 08:33:29 CEST 2014 10 finished
Sun Jun 29 08:33:29 CEST 2014 7 started
java.util.concurrent.TimeoutException
    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:228)
    at java.util.concurrent.FutureTask.get(FutureTask.java:91)
    at callabletest.CallableTest2.main(CallableTest2.java:29)
Sun Jun 29 08:33:36 CEST 2014 7 finished
Sun Jun 29 08:33:36 CEST 2014 added 7
Sun Jun 29 08:33:36 CEST 2014 6 started
Sun Jun 29 08:33:36 CEST 2014 8 finished
Sun Jun 29 08:33:36 CEST 2014 5 started
java.util.concurrent.TimeoutException
    at java.util.concurrent.FutureTask$Sync.innerGet(Sun Jun 29 08:33:41 CEST 2014 5 finished
FutureTask.java:228)
Sun Jun 29 08:33:41 CEST 2014 added 5
    at java.util.concurrent.FutureTask.get(FutureTask.java:91)
Sun Jun 29 08:33:41 CEST 2014 4 started
    at callabletest.CallableTest2.main(CallableTest2.java:29)
Sun Jun 29 08:33:42 CEST 2014 6 finished
Sun Jun 29 08:33:42 CEST 2014 3 started
Sun Jun 29 08:33:45 CEST 2014 3 finished
Sun Jun 29 08:33:45 CEST 2014 2 started
Sun Jun 29 08:33:45 CEST 2014 4 finished
Sun Jun 29 08:33:45 CEST 2014 added 4
Sun Jun 29 08:33:45 CEST 2014 added 3
Sun Jun 29 08:33:45 CEST 2014 1 started
Sun Jun 29 08:33:46 CEST 2014 1 finished
Sun Jun 29 08:33:47 CEST 2014 2 finished
Sun Jun 29 08:33:47 CEST 2014 added 2
Sun Jun 29 08:33:47 CEST 2014 added 1
Sun Jun 29 08:33:47 CEST 2014 results:
Sun Jun 29 08:33:47 CEST 2014 9
Sun Jun 29 08:33:47 CEST 2014 7
Sun Jun 29 08:33:47 CEST 2014 5
Sun Jun 29 08:33:47 CEST 2014 4
Sun Jun 29 08:33:47 CEST 2014 3
Sun Jun 29 08:33:47 CEST 2014 2
Sun Jun 29 08:33:47 CEST 2014 1

Теперь я получаю некоторые исключения TimeoutException, но не там, где я их ожидаю. Например. Callable, работающий 9 и 7 секунд, не генерирует исключение!

Что мне нужно изменить в коде, чтобы получать только результаты коротких потоков и убивать длинные. В примере только результаты 1-5 без 6-10.

Я проверил много вещей, но я не могу заставить его работать. Пожалуйста помоги


Это ответ на сообщение bstar55 с использованием ScheduledExecutorService.

Я изменил свой код в отношении вашего намека на:

public class CallableTest3 {

    public static void main(String[] args) {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
        List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
        
        for (int i = 10; i > 0; i--) {
            Job job = new Job(i * 1000, i);
            final Future handler = executor.submit(job);
            final int x = i;
            executor.schedule(new Runnable() {

                public void run() {
                    boolean cancel = handler.cancel(true);
                    if(cancel){
                        System.out.println(new Date() + " job " + x + " cancelled");
                    }else{
                        System.out.println(new Date() + " job " + x + " not cancelled");
                    }
                }
            }, 5000, TimeUnit.MILLISECONDS);
            futures.add(handler);
        }

        ArrayList<Integer> results = new ArrayList<Integer>();
        for (Future<Integer> future : futures) {
            try {
                Integer content = future.get(5, TimeUnit.SECONDS);
                results.add(content);
                System.out.println(new Date() + " added " + content);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        executor.shutdown();

        System.out.println(new Date() + " results:");
        for (int j : results) {
            System.out.println(new Date() + " --- " + j);
        }
    }
}

Но это также не работает, как ожидалось. Результат:

Sun Jun 29 10:27:41 CEST 2014 9 started
Sun Jun 29 10:27:41 CEST 2014 10 started
java.util.concurrent.TimeoutException
    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:228)
    at java.util.concurrent.FutureTask.get(FutureTask.java:91)
    at callabletest.CallableTest3.main(CallableTest3.java:43)
Sun Jun 29 10:27:50 CEST 2014 9 finished
Sun Jun 29 10:27:50 CEST 2014 added 9
Sun Jun 29 10:27:50 CEST 2014 8 started
Sun Jun 29 10:27:51 CEST 2014 10 finished
Sun Jun 29 10:27:51 CEST 2014 7 started
java.util.concurrent.TimeoutException
    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:228)
    at java.util.concurrent.FutureTask.get(FutureTask.java:91)
    at callabletest.CallableTest3.main(CallableTest3.java:43)
Sun Jun 29 10:27:58 CEST 2014 8 finished
Sun Jun 29 10:27:58 CEST 2014 6 started
Sun Jun 29 10:27:58 CEST 2014 7 finished
Sun Jun 29 10:27:58 CEST 2014 5 started
Sun Jun 29 10:27:58 CEST 2014 added 7
Sun Jun 29 10:28:03 CEST 2014 5 finished
Sun Jun 29 10:28:03 CEST 2014 4 started
java.util.concurrent.TimeoutException
    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:228)
    at java.util.concurrent.FutureTask.get(FutureTask.java:91)
Sun Jun 29 10:28:03 CEST 2014 added 5
    at callabletest.CallableTest3.main(CallableTest3.java:43)
Sun Jun 29 10:28:04 CEST 2014 6 finished
Sun Jun 29 10:28:04 CEST 2014 3 started
Sun Jun 29 10:28:07 CEST 2014 3 finished
Sun Jun 29 10:28:07 CEST 2014 2 started
Sun Jun 29 10:28:07 CEST 2014 4 finished
Sun Jun 29 10:28:07 CEST 2014 added 4
Sun Jun 29 10:28:07 CEST 2014 added 3
Sun Jun 29 10:28:07 CEST 2014 1 started
java.util.concurrent.CancellationException
    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:230)
    at java.util.concurrent.FutureTask.get(FutureTask.java:91)
    at callabletest.CallableTest3.main(CallableTest3.java:43)
Sun Jun 29 10:28:08 CEST 2014 1 finished
Sun Jun 29 10:28:08 CEST 2014 job 10 not cancelled
Sun Jun 29 10:28:08 CEST 2014 job 9 not cancelled
Sun Jun 29 10:28:08 CEST 2014 job 8 not cancelled
Sun Jun 29 10:28:08 CEST 2014 job 7 not cancelled
Sun Jun 29 10:28:08 CEST 2014 job 6 not cancelled
Sun Jun 29 10:28:08 CEST 2014 job 5 not cancelled
Sun Jun 29 10:28:08 CEST 2014 job 4 not cancelled
Sun Jun 29 10:28:08 CEST 2014 job 3 not cancelled
Sun Jun 29 10:28:08 CEST 2014 2 interrupted
Sun Jun 29 10:28:08 CEST 2014 job 1 not cancelled
Sun Jun 29 10:28:08 CEST 2014 added 1
Sun Jun 29 10:28:08 CEST 2014 results:
Sun Jun 29 10:28:08 CEST 2014 --- 9
Sun Jun 29 10:28:08 CEST 2014 --- 7
Sun Jun 29 10:28:08 CEST 2014 --- 5
Sun Jun 29 10:28:08 CEST 2014 --- 4
Sun Jun 29 10:28:08 CEST 2014 --- 3
Sun Jun 29 10:28:08 CEST 2014 --- 1
Sun Jun 29 10:28:08 CEST 2014 job 2 cancelled

Но вместо этого задание 2 было отменено!



person user3787186    schedule 29.06.2014    source источник
comment
Регистрируйте, когда задания отправляются и когда задания берутся из пула. Если вам интересно узнать, как долго выполняется первое задание до вызова future.get().   -  person bstar55    schedule 29.06.2014
comment
Ладно, кажется, я начинаю понимать, что происходит. Вы запускаете задания в двух потоках, а затем пытаетесь получить результаты в одном потоке. Пока вы ждете одного результата, по крайней мере еще одно задание выполняется без вашего ожидания. К тому времени, когда вы начинаете ждать результата, задание, которого вы ждете, уже выполнялось в течение некоторого времени, что делает недействительным ваш 5-секундный тайм-аут.   -  person bstar55    schedule 29.06.2014


Ответы (4)


Я предлагаю вам разделить вашу проблему на 2 отдельные:

  1. работать в нескольких потоках
  2. использовать тайм-аут для каждой операции

Для первого (многопоточность) вы уже использовали исполнителя службы, который может управлять этим на 2 потоках: Executors.newFixedThreadPool(2). Если вы примените тайм-аут здесь, тайм-аут будет действовать для запуска всех задач, но вам нужен тайм-аут для каждого задания.

Что касается проблемы с тайм-аутом, вы можете справиться с ней благодаря новому исполнителю службы для каждого задания в классе: JobManager.

package com.stackoverflow.q24473796;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class JobManager implements Callable<Integer> {

protected long timeout;
protected TimeUnit timeUnit;
protected Callable<Integer> job;

public JobManager(long timeout, TimeUnit timeUnit, Callable<Integer> job) {
this.timeout = timeout;
this.timeUnit = timeUnit;
this.job = job;
}

@Override
public Integer call() {
    Integer result = new Integer(-1); // default, this could be adapted
    ExecutorService exec = Executors.newSingleThreadExecutor();

    try {
        result = exec.submit(job).get(timeout, timeUnit);
    } catch (InterruptedException | ExecutionException | TimeoutException e) {
        // Whatever you want
        if (e instanceof TimeoutException) {
            System.out.println("Timeout get for " + job.toString());
        } else {
            System.out.println("exception get for " + job.toString() + " : " + e.getMessage());
        }

    }
    exec.shutdown();
    return result;
    }
}

Затем вы можете вызывать задачи из основного потока следующим образом:

    Job job = new Job(i * 1000, i);
    Future<Integer> future = newFixedThreadPool.submit(new JobManager(5, TimeUnit.SECONDS, job));

Я добавил ваш CallableTest: package com.stackoverflow.q24473796;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class CallableTest {

    public static void main(String[] args) {
        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);

        List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
        for (int i = 10; i > 0; i--) {
            Job job = new Job(i * 1000, i);
            Future<Integer> future = newFixedThreadPool.submit(new   JobManager(5, TimeUnit.SECONDS, job));
            futures.add(future);
        }

        ArrayList<Integer> results = new ArrayList<Integer>();
        for (Future<Integer> future : futures) {
            Integer result = new Integer(-1);
            try {
                result = future.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
            if (result != -1) {
                results.add(result);
            }
        }

        newFixedThreadPool.shutdown();

        try {
            newFixedThreadPool.awaitTermination(60, TimeUnit.SECONDS); //Global Timeout
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(new Date() + " results:");
        for (int j : results) {
            System.out.println(new Date() + " " + j);
        }
    }
}

И вы получите следующий вывод:

Wed Apr 29 10:51:02 CEST 2015 10 started
Wed Apr 29 10:51:02 CEST 2015 9 started
Timeout get for com.stackoverflow.q24473796.Job@249fe45c
Timeout get for com.stackoverflow.q24473796.Job@249fe45c
Wed Apr 29 10:51:07 CEST 2015 8 started
Wed Apr 29 10:51:07 CEST 2015 7 started
Wed Apr 29 10:51:11 CEST 2015 9 finished
Timeout get for com.stackoverflow.q24473796.Job@3cd4c5a0
Timeout get for com.stackoverflow.q24473796.Job@3cd4c5a0
Wed Apr 29 10:51:12 CEST 2015 6 started
Wed Apr 29 10:51:12 CEST 2015 5 started
Wed Apr 29 10:51:12 CEST 2015 10 finished
Wed Apr 29 10:51:14 CEST 2015 7 finished
Wed Apr 29 10:51:15 CEST 2015 8 finished
Wed Apr 29 10:51:17 CEST 2015 5 finished
Wed Apr 29 10:51:17 CEST 2015 4 started
Timeout get for com.stackoverflow.q24473796.Job@2a0fded2
Wed Apr 29 10:51:17 CEST 2015 3 started
Wed Apr 29 10:51:18 CEST 2015 6 finished
Wed Apr 29 10:51:20 CEST 2015 3 finished
Wed Apr 29 10:51:20 CEST 2015 2 started
Wed Apr 29 10:51:21 CEST 2015 4 finished
Wed Apr 29 10:51:21 CEST 2015 1 started
Wed Apr 29 10:51:22 CEST 2015 1 finished
Wed Apr 29 10:51:22 CEST 2015 2 finished
Wed Apr 29 10:51:22 CEST 2015 results:
Wed Apr 29 10:51:22 CEST 2015 5
Wed Apr 29 10:51:22 CEST 2015 4
Wed Apr 29 10:51:22 CEST 2015 3
Wed Apr 29 10:51:22 CEST 2015 2
Wed Apr 29 10:51:22 CEST 2015 1
person Loic Mouchard    schedule 29.04.2015

У вас есть задания, работающие в двух потоках, и вы читаете результаты из основного потока. Пока вы ждете завершения одного задания, другое задание выполняется без вашего ожидания. Я думаю, вы обнаружите, что получите ожидаемые результаты, если измените размер пула потоков на 1.

Если вам нужно запускать задания в нескольких потоках, вам придется придумать способ отслеживать, как долго задание уже выполняется к тому времени, когда вы начинаете ждать результата. Если задание уже выполняется дольше 5 секунд, отклоните его. Если нет, подождите 5 секунд минус количество времени, в течение которого он уже работает.

Один из вариантов — сохранить время начала в задании. Тогда вы могли бы сделать что-то вроде

 long elapsedTime = System.currentTimeMillis() - job.getStartTime();
 if (elapsedTime < 5000) {
     future.get(5000 - elapsedTime, TimeUnit.MILLISECONDS);
 }
person bstar55    schedule 29.06.2014
comment
Думаю идея хорошая. Как я могу сохранить время начала в Callable, чтобы я мог использовать его в вызывающем основном потоке для сравнения? Но это не сработает для ExecutorCompletionService, потому что возвращаемые объекты Future находятся не в том порядке, в котором они отправляются в пул. Поэтому я не знаю, какой объект Future относится к тому, что вызывает Callable для сравнения таймингов. - person user3787186; 29.06.2014
comment
Да, я также не вижу способа получить работу, связанную с будущим. Я опубликовал еще один ответ, который может оказаться более полезным. - person bstar55; 29.06.2014

Для этого вы можете использовать ScheduledExecutorService. Сначала вы должны отправить каждую задачу и сохранить созданные фьючерсы. После этого вы можете отправить новую задачу, которая через некоторое время отменит сохраненное будущее.

 ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); 
 for (int i = 10; i > 0; i--) {
        Job job = new Job(i * 1000, i);
        final Future handler = executor.submit(job);
        executor.schedule(new Runnable(){
            public void run(){
                handler.cancel();
            }
        }, 5000, TimeUnit.MILLISECONDS);
        futures.add(handler);
 }

Это выполнит ваш обработчик (основная функциональность будет прервана) в течение 5 секунд, а затем отменит (т.е. прервет) эту конкретную задачу.

На данный момент вы знаете, что ни одно задание не может выполняться более 5 секунд, поэтому

Integer content = future.get(5, TimeUnit.SECONDS);

Должен работать нормально.

person bstar55    schedule 29.06.2014
comment
Черт, из-за моей низкой репутации я не могу ответить на свой пост в течение 8 часов здесь. Поэтому я добавлю свои изменения в свой исходный пост. - person user3787186; 29.06.2014

Вероятно, лучший способ - управлять временем из метода call() объекта Callable. Когда вы вводите метод, записывайте время. Разделите время в методе call() — в примере вместо того, чтобы спать все время, спите по одной секунде за раз — и каждый раз проверяйте, не истекло ли максимальное время с момента входа. Если это так, прервите обработку и выйдите изящно.

Если вы абсолютно не можете периодически проверять код Callable во время - или во внешнем уведомлении об остановке, как предлагали другие, - есть один способ завершить его из кода, выполняющегося вне потока. Однако этот способ устарел, потому что он почти наверняка вызовет неисправимые ошибки. По сути, когда поток запускается, то есть когда вводится метод call(), запустите подкласс TimerTask и передайте ему указатель на поток. Когда TimerTask срабатывает, он вызывает Thread.stop() в потоке. Опять же, это очень небезопасно, потому что объекты могут остаться в поврежденном состоянии, как описано в документации Oracle здесь:

http://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html

Практически единственный способ, которым это могло бы работать, - это если код вашего Callable никогда не нуждается в синхронизации и никогда не синхронизируется на каком-либо объекте, даже в вызове библиотеки, и никогда не будет изменен в будущем для этого. Это чрезвычайно сложно обеспечить, поэтому было бы намного лучше найти способ разбить код Callable, чтобы он мог периодически проверять время и корректно завершать работу, если время истекло.

И последнее замечание: если вы действительно считаете, что код вашего Callable никогда не нуждается в синхронизации для совместного использования объектов в памяти, это отличный кандидат на выполнение во внешнем процессе, а не в потоке основного процесса. Преимущество этого в том, что вы можете относительно безопасно убить внешний процесс. По сути, TimerTask во внешнем процессе будет вызывать System.exit() в рабочем процессе, а не Thread.stop() в рабочем потоке.

person Warren Dew    schedule 29.06.2014
comment
Да, я также думал о поиске времени в вызываемом. Проблема в том, что в реальном приложении вместо простого Thread.sleep(x) используется большой блок кода; Я не знаю, как проверять этот кодовый блок каждую секунду, не создавая новый поток для каждого потока! - person user3787186; 29.06.2014
comment
Вы хотите, чтобы потоки останавливались через 5 секунд, чтобы сэкономить время обработки, или все в порядке, если они продолжают работать, и у вас просто есть возможность игнорировать результат, если они работали дольше 5 секунд без завершения? - person Warren Dew; 29.06.2014
comment
Нити должны быть остановлены. В реальном приложении Callable иногда зацикливается. Поэтому мне также нужно убить эти потоки после моего тайм-аута. Реальный тайм-аут составляет около получаса, так что это не проблема реального времени или около того. Результат этого убитого callable меня больше не интересует. Но было бы также интересно, кого из Callable убили. - person user3787186; 29.06.2014
comment
Способ есть, но очень опасный. Существует также альтернатива использованию процессов вместо потоков. См. правки к моему ответу для более подробной информации. - person Warren Dew; 29.06.2014