Як тайм-аут потоку


255

Я хочу запустити потік протягом певного фіксованого часу. Якщо вона не буде завершена протягом цього часу, я хочу або вбити, викинути якийсь виняток, або якось поправити. Як це можна зробити?

Один із способів зробити це, як я зрозумів з цієї теми, - це використовувати TimerTask всередині методу run () Thread.

Чи є для цього кращі рішення?

 
EDIT: Додавання щедрості, оскільки мені потрібна була чіткіша відповідь. Код ExecutorService, наведений нижче, не вирішує мою проблему. Чому я повинен спати () після виконання (якийсь код - я не маю ручки над цим фрагментом коду)? Якщо код завершено, а режим сну () перервано, як це може бути timeOut?

Завдання, яке потрібно виконати, не контролюється. Це може бути будь-який фрагмент коду. Проблема полягає в тому, що цей фрагмент коду може зіткнутися з нескінченним циклом. Я не хочу, щоб це сталося. Отже, я просто хочу виконати це завдання в окремому потоці. Батьківський потік повинен зачекати, поки цей потік закінчиться, і він повинен знати статус завдання (тобто, чи вийшов тайм-аут, чи якийсь виняток стався, або якщо його успіх). Якщо завдання переходить у нескінченний цикл, моя батьківська нитка продовжує чекати нескінченно, що не є ідеальною ситуацією.


EDIT: Додавання щедрості, як мені потрібна більш чітка відповідь. наведений нижче код ExecutorService не вирішує мою проблему. Чому я повинен спати () після виконання свого коду? Якщо код завершено, а режим сну () перервано, як це може бути timeOut?
java_geek

7
Це sleep()була лише заглушка, яка представляла «завдання, що триває довгий час». Просто замініть його своїм справжнім завданням;)
BalusC

1
... "тривалий час виконання завдання", який трапляється реагувати на interrupt()дзвінки в його потоці ... не всі "блокуючі" дзвінки роблять, як я намагався вказати у своїй відповіді. Специфіка завдання, яке ви намагаєтеся скасувати, має величезну зміну в підході, який слід використовувати. Більше інформації про завдання було б корисно.
erickson

Якщо ці відповіді не вирішують проблему, я думаю, більше відповідей / код повинен допомогти відповісти.
Елістер

Ці нитки, які ви хочете обмежити часом; вони блокують дзвінки, чи вони перебувають у певному циклі, де ви можете легко перевірити якусь змінну, щоб побачити, чи настав час вийти?
Скотт Сміт

Відповіді:


376

Дійсно, скоріше використовувати ExecutorServiceзамість Timer, ось SSCCE :

package com.stackoverflow.q2275443;

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

public class Test {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new Task());

        try {
            System.out.println("Started..");
            System.out.println(future.get(3, TimeUnit.SECONDS));
            System.out.println("Finished!");
        } catch (TimeoutException e) {
            future.cancel(true);
            System.out.println("Terminated!");
        }

        executor.shutdownNow();
    }
}

class Task implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(4000); // Just to demo a long running task of 4 seconds.
        return "Ready!";
    }
}

Пограйте трохи з timeoutаргументом у Future#get()методі, наприклад, збільште його до 5, і ви побачите, що нитка закінчується. Ви можете перехопити тайм-аут у catch (TimeoutException e)блоці.

Оновлення: для уточнення концептуального непорозуміння, sleep()це не потрібно. Він просто використовується для SSCCE / демонстраційних цілей. Просто виконайте своє довге завдання прямо там sleep(). Всередині вашого тривалого завдання слід перевірити, чи не переривається нитка так:

while (!Thread.interrupted()) {
    // Do your long running task here.
}

24
Замініть Thread.sleep(4000)якусь іншу тривалу заяву, і приклад не буде працювати. Іншими словами, цей приклад може працювати лише в тому випадку, якщо пристрій Taskпризначений для розуміння Thread.isInterrupted()зміни статусу.
yegor256

@BalusC Я спробував такий підхід, намагаючись припинити свої теми, але не міг змусити його працювати. Ви можете перевірити його тут: stackoverflow.com/questions/35553420 / ...
syfantid

Як керується InterruptException майбутнім.cancel (істина)?
болеї

1
n кількість людей прокоментували назву пакета, і ось ще одна +1 для нього. Ось така приємна майстерність бути захопленою. Дякую!
Ешвін Тумма

@BalusC У мене є сумніви, чи буде майбутнє виконуватись синхронно і якщо це займе більше заздалегідь визначеного часу, то воно буде припинено. Інакше це буде виконано в майбутньому, а тим часом ми розраховуємо на час ... Дякую
Адель Ахмед

49

Не існує 100% надійного способу зробити це для будь-якого старого завдання. Завдання має бути написане з урахуванням цієї здатності.

Основні бібліотеки Java, як ExecutorServiceскасування асинхронних завдань із interrupt()викликами в робочій нитці. Так, наприклад, якщо завдання містить певний цикл, вам слід перевірити його стан переривання на кожній ітерації. Якщо завдання виконує операції вводу / виводу, вони також повинні бути переривними - і налаштування цього налаштування може бути складним. У будь-якому випадку пам’ятайте, що код повинен активно перевіряти на наявність перебоїв; встановлення переривання не обов'язково нічого робити.

Звичайно, якщо у вас є певний простий цикл, ви можете просто перевірити поточний час на кожній ітерації та відмовитися, коли минув визначений час. Робоча нитка в цьому випадку не потрібна.


На мій досвід, єдиний код, який не реагує на початок переривання, - це блокування власного коду (очікування операційної системи).
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersen Я згоден, але це дуже багато коду. Моя думка, що для цього не існує механізму загального призначення; ви повинні зрозуміти політику переривання завдання.
erickson

@erickson, я згоден з вашим. На точку відповіді: Для кожної задачі має бути визначена політика скасування, якщо ви зацікавлені в тому, щоб зупинити її. Або нитка повинна знати про те, що вона повинна робити, коли вона переривається. Зрештою, переривання та зупинка будь-якого потоку - це лише запит, який цільовий потік може прийняти чи відхилити, тому краще написати завдання, враховуючи це.
AKS

чи не може виконавець виконувати завдання для виклику потоку? також виконавець може вирішити виконати завдання десь у майбутньому?
filthy_wizard

@ user1232726 execute()Метод батьківського інтерфейсу, Executorможе виконувати завдання в потоці виклику. Немає подібних тверджень для submit()методів ExecutorServiceповернення Futureекземплярів. Під сервісом розуміється те, що є робочі потоки, які потрібно очистити за допомогою відключення, і що завдання виконуються асинхронно. Це означає, що в договорі немає нічого, що говорить про те, що ExecutorServiceвиконувати завдання заборонено виконувати завдання; ці гарантії походять від API впровадження, як і на Executorsзаводах.
erickson

13

Подумайте про використання екземпляра ExecutorService . І параметри, invokeAll()і invokeAny()методи доступні з timeoutпараметром.

Поточний потік буде блокуватися до тих пір, поки метод не завершиться (не впевнений, що це бажано) або тому, що завдання (задачі) виконано нормально або було досягнуто тайм-аут. Ви можете оглянути повернених Future(-ів), щоб визначити, що сталося.


9

Якщо припустимо, що код потоку перебуває поза вашим контролем:

Із згаданої вище документації на Java :

Що робити, якщо нитка не відповідає на Thread.interrupt?

У деяких випадках можна використовувати конкретні прийоми з додатком. Наприклад, якщо нитка чекає на відомій розетці, ви можете закрити сокет, щоб змусити повернути нитку негайно. На жаль, насправді не існує жодної техніки, яка б працювала загалом. Слід зазначити, що у всіх ситуаціях, коли нитка очікування не відповідає на Thread.interrupt, вона також не відповість Thread.stop. Такі випадки включають навмисні атаки відмови в обслуговуванні та операції вводу / виводу, для яких thread.stop та thread.interrupt працюють не належним чином.

Нижня лінія:

Переконайтеся, що всі потоки можуть бути перервані, інакше вам потрібні конкретні знання про нитку, як-от встановити прапор. Можливо, ви можете зажадати, щоб завдання було надано вам разом з кодом, необхідним для його зупинки - визначте інтерфейс із stop()методом. Ви також можете попередити, коли вам не вдалося зупинити завдання.


8

BalusC сказав:

Оновлення: для уточнення концептуального непорозуміння сон () не потрібен. Він просто використовується для SSCCE / демонстраційних цілей. Просто виконайте своє довге завдання прямо на місці сну ().

Але якщо ви заміните Thread.sleep(4000);на, for (int i = 0; i < 5E8; i++) {}він не компілюється, оскільки порожній цикл не кидає InterruptedException.

А щоб нитка була переривною, її потрібно кинути InterruptedException.

Це здається мені серйозною проблемою. Я не бачу, як адаптувати цю відповідь до роботи із загальним довготривалим завданням.

Відредаговано, щоб додати: Я переосмислив це як нове запитання: [ перериваючи потік через встановлений час, чи потрібно викидати InterruptException? ]


Як я це роблю, це додати «Виняток по шляху» у загальнодоступний метод <T> call {} класу
Роберто Лінарес

5

Я думаю, вам слід поглянути на належні механізми обробки одночасності (нитки, що впадають у нескінченні петлі, самі по собі не виглядають добре, btw). Переконайтеся, що ви прочитали трохи про тему "вбивство" або "зупинка" теми .

Те, що ви описуєте, дуже схоже на "рандеву", тому, можливо, ви захочете поглянути на CyclicBarrier .

Можуть бути й інші конструкції (наприклад, наприклад, використовуючи CountDownLatch ), які можуть вирішити вашу проблему (один потік, який чекає з таймаутом на засувку, інший повинен відраховувати засувку, якщо вона виконала свою роботу, яка випустить ваш перший потік або після тайм-аут або коли викликається зворотний відлік засувки).

Зазвичай я рекомендую дві книги в цій галузі: Паралельне програмування на Java та Java Concurrency in Practice .


5

Я створив клас помічників саме для цього деякий час тому. Чудово працює:

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
 * TimeOut class - used for stopping a thread that is taking too long
 * @author Peter Goransson
 *
 */
public class TimeOut {

    Thread interrupter;
    Thread target;
    long timeout;
    boolean success;
    boolean forceStop;

    CyclicBarrier barrier;

    /**
     * 
     * @param target The Runnable target to be executed
     * @param timeout The time in milliseconds before target will be interrupted or stopped
     * @param forceStop If true, will Thread.stop() this target instead of just interrupt() 
     */
    public TimeOut(Runnable target, long timeout, boolean forceStop) {      
        this.timeout = timeout;
        this.forceStop = forceStop;

        this.target = new Thread(target);       
        this.interrupter = new Thread(new Interrupter());

        barrier = new CyclicBarrier(2); // There will always be just 2 threads waiting on this barrier
    }

    public boolean execute() throws InterruptedException {  

        // Start target and interrupter
        target.start();
        interrupter.start();

        // Wait for target to finish or be interrupted by interrupter
        target.join();  

        interrupter.interrupt(); // stop the interrupter    
        try {
            barrier.await(); // Need to wait on this barrier to make sure status is set
        } catch (BrokenBarrierException e) {
            // Something horrible happened, assume we failed
            success = false;
        } 

        return success; // status is set in the Interrupter inner class
    }

    private class Interrupter implements Runnable {

        Interrupter() {}

        public void run() {
            try {
                Thread.sleep(timeout); // Wait for timeout period and then kill this target
                if (forceStop) {
                  target.stop(); // Need to use stop instead of interrupt since we're trying to kill this thread
                }
                else {
                    target.interrupt(); // Gracefully interrupt the waiting thread
                }
                System.out.println("done");             
                success = false;
            } catch (InterruptedException e) {
                success = true;
            }


            try {
                barrier.await(); // Need to wait on this barrier
            } catch (InterruptedException e) {
                // If the Child and Interrupter finish at the exact same millisecond we'll get here
                // In this weird case assume it failed
                success = false;                
            } 
            catch (BrokenBarrierException e) {
                // Something horrible happened, assume we failed
                success = false;
            }

        }

    }
}

Це називається так:

long timeout = 10000; // number of milliseconds before timeout
TimeOut t = new TimeOut(new PhotoProcessor(filePath, params), timeout, true);
try {                       
  boolean sucess = t.execute(); // Will return false if this times out
  if (!sucess) {
    // This thread timed out
  }
  else {
    // This thread ran completely and did not timeout
  }
} catch (InterruptedException e) {}  

3

Я надсилаю вам фрагмент коду, який показує спосіб вирішення проблеми. Як приклад я читаю файл. Ви можете використовувати цей метод для іншої операції, але вам потрібно реалізувати метод kill (), щоб основна операція була перервана.

сподіваюся, що це допомагає


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

/**
 * Main class
 * 
 * @author el
 * 
 */
public class Main {
    /**
     * Thread which perform the task which should be timed out.
     * 
     * @author el
     * 
     */
    public static class MainThread extends Thread {
        /**
         * For example reading a file. File to read.
         */
        final private File fileToRead;
        /**
         * InputStream from the file.
         */
        final private InputStream myInputStream;
        /**
         * Thread for timeout.
         */
        final private TimeOutThread timeOutThread;

        /**
         * true if the thread has not ended.
         */
        boolean isRunning = true;

        /**
         * true if all tasks where done.
         */
        boolean everythingDone = false;

        /**
         * if every thing could not be done, an {@link Exception} may have
         * Happens.
         */
        Throwable endedWithException = null;

        /**
         * Constructor.
         * 
         * @param file
         * @throws FileNotFoundException
         */
        MainThread(File file) throws FileNotFoundException {
            setDaemon(false);
            fileToRead = file;
            // open the file stream.
            myInputStream = new FileInputStream(fileToRead);
            // Instantiate the timeout thread.
            timeOutThread = new TimeOutThread(10000, this);
        }

        /**
         * Used by the {@link TimeOutThread}.
         */
        public void kill() {
            if (isRunning) {
                isRunning = false;
                if (myInputStream != null) {
                    try {
                        // close the stream, it may be the problem.
                        myInputStream.close();
                    } catch (IOException e) {
                        // Not interesting
                        System.out.println(e.toString());
                    }
                }
                synchronized (this) {
                    notify();
                }
            }
        }

        /**
         * The task which should be timed out.
         */
        @Override
        public void run() {
            timeOutThread.start();
            int bytes = 0;
            try {
                // do something
                while (myInputStream.read() >= 0) {
                    // may block the thread.
                    myInputStream.read();
                    bytes++;
                    // simulate a slow stream.
                    synchronized (this) {
                        wait(10);
                    }
                }
                everythingDone = true;
            } catch (IOException e) {
                endedWithException = e;
            } catch (InterruptedException e) {
                endedWithException = e;
            } finally {
                timeOutThread.kill();
                System.out.println("-->read " + bytes + " bytes.");
                isRunning = false;
                synchronized (this) {
                    notifyAll();
                }
            }
        }
    }

    /**
     * Timeout Thread. Kill the main task if necessary.
     * 
     * @author el
     * 
     */
    public static class TimeOutThread extends Thread {
        final long timeout;
        final MainThread controlledObj;

        TimeOutThread(long timeout, MainThread controlledObj) {
            setDaemon(true);
            this.timeout = timeout;
            this.controlledObj = controlledObj;
        }

        boolean isRunning = true;

        /**
         * If we done need the {@link TimeOutThread} thread, we may kill it.
         */
        public void kill() {
            isRunning = false;
            synchronized (this) {
                notify();
            }
        }

        /**
         * 
         */
        @Override
        public void run() {
            long deltaT = 0l;
            try {
                long start = System.currentTimeMillis();
                while (isRunning && deltaT < timeout) {
                    synchronized (this) {
                        wait(Math.max(100, timeout - deltaT));
                    }
                    deltaT = System.currentTimeMillis() - start;
                }
            } catch (InterruptedException e) {
                // If the thread is interrupted,
                // you may not want to kill the main thread,
                // but probably yes.
            } finally {
                isRunning = false;
            }
            controlledObj.kill();
        }
    }

    /**
     * Start the main task and wait for the end.
     * 
     * @param args
     * @throws FileNotFoundException
     */
    public static void main(String[] args) throws FileNotFoundException {
        long start = System.currentTimeMillis();
        MainThread main = new MainThread(new File(args[0]));
        main.start();
        try {
            while (main.isRunning) {
                synchronized (main) {
                    main.wait(1000);
                }
            }
            long stop = System.currentTimeMillis();

            if (main.everythingDone)
                System.out.println("all done in " + (stop - start) + " ms.");
            else {
                System.out.println("could not do everything in "
                        + (stop - start) + " ms.");
                if (main.endedWithException != null)
                    main.endedWithException.printStackTrace();
            }
        } catch (InterruptedException e) {
            System.out.println("You've killed me!");
        }
    }
}

З повагою


3

Ось мій дуже простий у використанні хелперний клас для запуску або виклику фрагмента коду Java :-)

Це засновано на чудовому відповіді від BalusC

package com.mycompany.util.concurrent;

import java.util.concurrent.Callable;
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;
import java.util.concurrent.TimeoutException;

/**
 * Calling {@link Callable#call()} or Running {@link Runnable#run()} code
 * with a timeout based on {@link Future#get(long, TimeUnit))}
 * @author pascaldalfarra
 *
 */
public class CallableHelper
{

    private CallableHelper()
    {
    }

    public static final void run(final Runnable runnable, int timeoutInSeconds)
    {
        run(runnable, null, timeoutInSeconds);
    }

    public static final void run(final Runnable runnable, Runnable timeoutCallback, int timeoutInSeconds)
    {
        call(new Callable<Void>()
        {
            @Override
            public Void call() throws Exception
            {
                runnable.run();
                return null;
            }
        }, timeoutCallback, timeoutInSeconds); 
    }

    public static final <T> T call(final Callable<T> callable, int timeoutInSeconds)
    {
        return call(callable, null, timeoutInSeconds); 
    }

    public static final <T> T call(final Callable<T> callable, Runnable timeoutCallback, int timeoutInSeconds)
    {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        try
        {
            Future<T> future = executor.submit(callable);
            T result = future.get(timeoutInSeconds, TimeUnit.SECONDS);
            System.out.println("CallableHelper - Finished!");
            return result;
        }
        catch (TimeoutException e)
        {
            System.out.println("CallableHelper - TimeoutException!");
            if(timeoutCallback != null)
            {
                timeoutCallback.run();
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        catch (ExecutionException e)
        {
            e.printStackTrace();
        }
        finally
        {
            executor.shutdownNow();
            executor = null;
        }

        return null;
    }

}

2

Наступний фрагмент розпочне операцію окремим потоком, потім зачекайте до 10 секунд, щоб операція завершилася. Якщо операція не закінчиться вчасно, код спробує скасувати операцію, а потім продовжити її веселим шляхом. Навіть якщо операцію неможливо легко скасувати, батьківський потік не буде чекати, коли дочірній потік завершиться.

ExecutorService executorService = getExecutorService();
Future<SomeClass> future = executorService.submit(new Callable<SomeClass>() {
    public SomeClass call() {
        // Perform long-running task, return result. The code should check
        // interrupt status regularly, to facilitate cancellation.
    }
});
try {
    // Real life code should define the timeout as a constant or
    // retrieve it from configuration
    SomeClass result = future.get(10, TimeUnit.SECONDS);
    // Do something with the result
} catch (TimeoutException e) {
    future.cancel(true);
    // Perform other error handling, e.g. logging, throwing an exception
}

getExecutorService()Метод може бути реалізований декількома способами. Якщо у вас немає особливих вимог, ви можете просто зателефонувати Executors.newCachedThreadPool()за об'єднанням потоків без верхньої межі кількості потоків.


Для чого потрібен імпорт? Що таке SomeClassі Future?
ADTC

2

Одне, що я не бачив, це те, що вбивство ниток - це взагалі погана ідея. Існують методи для виготовлення потокових методів, які легко перериваються , але це зовсім інше, ніж просто вбити нитку після таймауту.

Ризик, що ви пропонуєте, полягає в тому, що ви, мабуть, не знаєте, в якому стані буде нитка, коли ви її вб'єте - тому ви ризикуєте внести нестабільність. Краще рішення - переконатися, що ваш потоковий код або не зависне, або добре відповість на запит про переривання.


Без контексту таке твердження звучить занадто обмежувально. В академічній обстановці мені дуже часто потрібно тестувати щось до часу очікування, і коли він виникає, я просто відкидаю всі обчислення і записую, що час очікування відбувся. Напевно, це рідкість у цій галузі, але все ж ...
Алессандро С.

@AlessandroS: це розумний момент, хоча ОП вимагає «кращих рішень», під якими я маю на увазі, що надійність та надійність надаються перевазі над грубою силою.
Dan Puzey

2

Чудова відповідь BalusC:

але просто додати, що сам час очікування не перериває нитку. навіть якщо ви перевіряєте за допомогою (! Thread.interrupted ()) у своєму завданні. якщо ви хочете переконатись, що нитка зупинена, ви також повинні переконатися, що future.cancel () викликається, коли вилучення виключення таймауту.

package com.stackoverflow.q2275443; 

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


public class Test { 
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new Task());

        try { 
            System.out.println("Started..");
            System.out.println(future.get(3, TimeUnit.SECONDS));
            System.out.println("Finished!");
        } catch (TimeoutException e) {
            //Without the below cancel the thread will continue to live 
            // even though the timeout exception thrown.
            future.cancel();
            System.out.println("Terminated!");
        } 

        executor.shutdownNow();
    } 
} 

class Task implements Callable<String> {
    @Override 
    public String call() throws Exception {
      while(!Thread.currentThread.isInterrupted()){
          System.out.println("Im still running baby!!");
      }          
    } 
} 

0

Я думаю, що відповідь головним чином залежить від самого завдання.

  • Це виконує одне завдання знову і знову?
  • Чи потрібно, щоб тайм-аут перервав виконання поточного завдання одразу після його закінчення?

Якщо перша відповідь - так, а друга - ні, ви можете зберегти її так просто:

public class Main {

    private static final class TimeoutTask extends Thread {
        private final long _timeoutMs;
        private Runnable _runnable;

        private TimeoutTask(long timeoutMs, Runnable runnable) {
            _timeoutMs = timeoutMs;
            _runnable = runnable;
        }

        @Override
        public void run() {
            long start = System.currentTimeMillis();
            while (System.currentTimeMillis() < (start + _timeoutMs)) {
                _runnable.run();
            }
            System.out.println("execution took " + (System.currentTimeMillis() - start) +" ms");
        }

    }

    public static void main(String[] args) throws Exception {
        new TimeoutTask(2000L, new Runnable() {

            @Override
            public void run() {
                System.out.println("doing something ...");
                try {
                    // pretend it's taking somewhat longer than it really does
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }
}

Якщо це не варіант, обмежте свої вимоги - або покажіть якийсь код.


0

Я шукав ExecutorService, який міг би перервати всі виконані ним вичерпані Runnables, але не знайшов жодного. Через кілька годин я створив його, як показано нижче. Цей клас можна модифікувати для підвищення надійності.

public class TimedExecutorService extends ThreadPoolExecutor {
    long timeout;
    public TimedExecutorService(int numThreads, long timeout, TimeUnit unit) {
        super(numThreads, numThreads, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(numThreads + 1));
        this.timeout = unit.toMillis(timeout);
    }

    @Override
    protected void beforeExecute(Thread thread, Runnable runnable) {
        Thread interruptionThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // Wait until timeout and interrupt this thread
                    Thread.sleep(timeout);
                    System.out.println("The runnable times out.");
                    thread.interrupt();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        interruptionThread.start();
    }
}

Використання:

public static void main(String[] args) {

    Runnable abcdRunnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("abcdRunnable started");
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                // logger.info("The runnable times out.");
            }
            System.out.println("abcdRunnable ended");
        }
    };

    Runnable xyzwRunnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("xyzwRunnable started");
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                // logger.info("The runnable times out.");
            }
            System.out.println("xyzwRunnable ended");
        }
    };

    int numThreads = 2, timeout = 5;
    ExecutorService timedExecutor = new TimedExecutorService(numThreads, timeout, TimeUnit.SECONDS);
    timedExecutor.execute(abcdRunnable);
    timedExecutor.execute(xyzwRunnable);
    timedExecutor.shutdown();
}

0

Зараз я зустрічаюся з таким питанням. Буває розшифрувати картинку. Процес декодування займає занадто багато часу, щоб екран залишався чорним. Додаю контролер часу: коли час занадто довгий, то спливає з поточної нитки. Далі є різниця:

   ExecutorService executor = Executors.newSingleThreadExecutor();
   Future<Bitmap> future = executor.submit(new Callable<Bitmap>() {
       @Override
       public Bitmap call() throws Exception {
       Bitmap bitmap = decodeAndScaleBitmapFromStream(context, inputUri);// do some time consuming operation
       return null;
            }
       });
       try {
           Bitmap result = future.get(1, TimeUnit.SECONDS);
       } catch (TimeoutException e){
           future.cancel(true);
       }
       executor.shutdown();
       return (bitmap!= null);

0

У мене була така ж проблема. Тому я придумав таке просте рішення.

public class TimeoutBlock {

 private final long timeoutMilliSeconds;
    private long timeoutInteval=100;

    public TimeoutBlock(long timeoutMilliSeconds){
        this.timeoutMilliSeconds=timeoutMilliSeconds;
    }

    public void addBlock(Runnable runnable) throws Throwable{
        long collectIntervals=0;
        Thread timeoutWorker=new Thread(runnable);
        timeoutWorker.start();
        do{ 
            if(collectIntervals>=this.timeoutMilliSeconds){
                timeoutWorker.stop();
                throw new Exception("<<<<<<<<<<****>>>>>>>>>>> Timeout Block Execution Time Exceeded In "+timeoutMilliSeconds+" Milli Seconds. Thread Block Terminated.");
            }
            collectIntervals+=timeoutInteval;           
            Thread.sleep(timeoutInteval);

        }while(timeoutWorker.isAlive());
        System.out.println("<<<<<<<<<<####>>>>>>>>>>> Timeout Block Executed Within "+collectIntervals+" Milli Seconds.");
    }

    /**
     * @return the timeoutInteval
     */
    public long getTimeoutInteval() {
        return timeoutInteval;
    }

    /**
     * @param timeoutInteval the timeoutInteval to set
     */
    public void setTimeoutInteval(long timeoutInteval) {
        this.timeoutInteval = timeoutInteval;
    }
}

Гарантує, що якщо блок не виконується протягом строку. процес припиняється і викидає виняток.

приклад:

try {
        TimeoutBlock timeoutBlock = new TimeoutBlock(10 * 60 * 1000);//set timeout in milliseconds
        Runnable block=new Runnable() {

            @Override
            public void run() {
                //TO DO write block of code 
            }
        };

        timeoutBlock.addBlock(block);// execute the runnable block 

    } catch (Throwable e) {
        //catch the exception here . Which is block didn't execute within the time limit
    }

0

У рішенні, наданому BalusC , основна нитка залишиться заблокованою протягом періоду очікування. Якщо у вас є пул потоків з більш ніж одним потоком, вам знадобиться така ж кількість додаткового потоку, який використовує Future.get (довгий час очікування, блок TimeUnit), блокуючи виклик, щоб зачекати і закрити потік, якщо він перевищує період очікування.

Загальне рішення цієї проблеми полягає у створенні ThreadPoolExecutor Decorator, який може додати функцію очікування. Цей клас «Декоратор» повинен створити стільки ниток, скільки має ThreadPoolExecutor, і всі ці теми потрібно використовувати лише для очікування та закриття ThreadPoolExecutor.

Загальний клас повинен бути реалізований як нижче:

import java.util.List;
import java.util.concurrent.*;

public class TimeoutThreadPoolDecorator extends ThreadPoolExecutor {


    private final ThreadPoolExecutor commandThreadpool;
    private final long timeout;
    private final TimeUnit unit;

    public TimeoutThreadPoolDecorator(ThreadPoolExecutor threadpool,
                                      long timeout,
                                      TimeUnit unit ){
        super(  threadpool.getCorePoolSize(),
                threadpool.getMaximumPoolSize(),
                threadpool.getKeepAliveTime(TimeUnit.MILLISECONDS),
                TimeUnit.MILLISECONDS,
                threadpool.getQueue());

        this.commandThreadpool = threadpool;
        this.timeout=timeout;
        this.unit=unit;
    }

    @Override
    public void execute(Runnable command) {
        super.execute(() -> {
            Future<?> future = commandThreadpool.submit(command);
            try {
                future.get(timeout, unit);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } catch (ExecutionException | TimeoutException e) {
                throw new RejectedExecutionException(e);
            } finally {
                future.cancel(true);
            }
        });
    }

    @Override
    public void setCorePoolSize(int corePoolSize) {
        super.setCorePoolSize(corePoolSize);
        commandThreadpool.setCorePoolSize(corePoolSize);
    }

    @Override
    public void setThreadFactory(ThreadFactory threadFactory) {
        super.setThreadFactory(threadFactory);
        commandThreadpool.setThreadFactory(threadFactory);
    }

    @Override
    public void setMaximumPoolSize(int maximumPoolSize) {
        super.setMaximumPoolSize(maximumPoolSize);
        commandThreadpool.setMaximumPoolSize(maximumPoolSize);
    }

    @Override
    public void setKeepAliveTime(long time, TimeUnit unit) {
        super.setKeepAliveTime(time, unit);
        commandThreadpool.setKeepAliveTime(time, unit);
    }

    @Override
    public void setRejectedExecutionHandler(RejectedExecutionHandler handler) {
        super.setRejectedExecutionHandler(handler);
        commandThreadpool.setRejectedExecutionHandler(handler);
    }

    @Override
    public List<Runnable> shutdownNow() {
        List<Runnable> taskList = super.shutdownNow();
        taskList.addAll(commandThreadpool.shutdownNow());
        return taskList;
    }

    @Override
    public void shutdown() {
        super.shutdown();
        commandThreadpool.shutdown();
    }
}

Вищеописаний декоратор можна використовувати як нижче:

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Main {

    public static void main(String[] args){

        long timeout = 2000;

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 10, 0, TimeUnit.MILLISECONDS, new SynchronousQueue<>(true));

        threadPool = new TimeoutThreadPoolDecorator( threadPool ,
                timeout,
                TimeUnit.MILLISECONDS);


        threadPool.execute(command(1000));
        threadPool.execute(command(1500));
        threadPool.execute(command(2100));
        threadPool.execute(command(2001));

        while(threadPool.getActiveCount()>0);
        threadPool.shutdown();


    }

    private static Runnable command(int i) {

        return () -> {
            System.out.println("Running Thread:"+Thread.currentThread().getName());
            System.out.println("Starting command with sleep:"+i);
            try {
                Thread.sleep(i);
            } catch (InterruptedException e) {
                System.out.println("Thread "+Thread.currentThread().getName()+" with sleep of "+i+" is Interrupted!!!");
                return;
            }
            System.out.println("Completing Thread "+Thread.currentThread().getName()+" after sleep of "+i);
        };

    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.