Как вызвать какой-либо метод блокировки с таймаутом в Java?


есть ли стандартный хороший способ вызвать метод блокировки с таймаутом в Java? Я хочу быть в состоянии сделать:

// call something.blockingMethod();
// if it hasn't come back within 2 seconds, forget it

если это имеет смысл.

спасибо.

10 79

10 ответов:

вы можете использовать исполнителя:

ExecutorService executor = Executors.newCachedThreadPool();
Callable<Object> task = new Callable<Object>() {
   public Object call() {
      return something.blockingMethod();
   }
};
Future<Object> future = executor.submit(task);
try {
   Object result = future.get(5, TimeUnit.SECONDS); 
} catch (TimeoutException ex) {
   // handle the timeout
} catch (InterruptedException e) {
   // handle the interrupts
} catch (ExecutionException e) {
   // handle other exceptions
} finally {
   future.cancel(true); // may or may not desire this
}

если future.get не возвращается через 5 секунд, он бросает TimeoutException. Тайм-аут может быть настроен в секундах, минутах, миллисекундах или любой единице, доступной в качестве константы в TimeUnit.

посмотреть документация подробнее.

вы можете обернуть вызов в FutureTask и использовать тайм-аут версии get ().

см.http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/FutureTask.html

существует также AspectJ решение для этого с jcabi-аспекты библиотека.

@Timeable(limit = 30, unit = TimeUnit.MINUTES)
public Soup cookSoup() {
  // Cook soup, but for no more than 30 minutes (throw and exception if it takes any longer
}

он не может быть более кратким, но вы должны зависеть от AspectJ и вводить его в свой жизненный цикл сборки, конечно.

есть статья, объясняющая это дальше:Ограничить Время Выполнения Метода Java

см. также Guava TimeLimiter который использует исполнителя за кулисами.

Thread thread = new Thread(new Runnable() {
    public void run() {
        something.blockingMethod();
    }
});
thread.start();
thread.join(2000);
if (thread.isAlive()) {
    thread.stop();
}

обратите внимание, что stop устарел, лучшей альтернативой является установка некоторого волатильного логического флага, внутри blockingMethod () проверьте его и выйдите, например:

import org.junit.*;
import java.util.*;
import junit.framework.TestCase;

public class ThreadTest extends TestCase {
    static class Something implements Runnable {
        private volatile boolean stopRequested;
        private final int steps;
        private final long waitPerStep;

        public Something(int steps, long waitPerStep) {
            this.steps = steps;
            this.waitPerStep = waitPerStep;
        }

        @Override
        public void run() {
            blockingMethod();
        }

        public void blockingMethod() {
            try {
                for (int i = 0; i < steps && !stopRequested; i++) {
                    doALittleBit();
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        public void doALittleBit() throws InterruptedException {
            Thread.sleep(waitPerStep);
        }

        public void setStopRequested(boolean stopRequested) {
            this.stopRequested = stopRequested;
        }
    }

    @Test
    public void test() throws InterruptedException {
        final Something somethingRunnable = new Something(5, 1000);
        Thread thread = new Thread(somethingRunnable);
        thread.start();
        thread.join(2000);
        if (thread.isAlive()) {
            somethingRunnable.setStopRequested(true);
            thread.join(2000);
            assertFalse(thread.isAlive());
        } else {
            fail("Exptected to be alive (5 * 1000 > 2000)");
        }
    }
}

попробуйте это. Более простое решение. Гарантирует, что если блок не был выполнен в течение срока. процесс завершится и вызовет исключение.

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
    }

предположим blockingMethod просто поспи немного Миллис:

public void blockingMethod(Object input) {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

мое решение-использовать wait() и synchronized такой:

public void blockingMethod(final Object input, long millis) {
    final Object lock = new Object();
    new Thread(new Runnable() {

        @Override
        public void run() {
            blockingMethod(input);
            synchronized (lock) {
                lock.notify();
            }
        }
    }).start();
    synchronized (lock) {
        try {
            // Wait for specific millis and release the lock.
            // If blockingMethod is done during waiting time, it will wake
            // me up and give me the lock, and I will finish directly.
            // Otherwise, when the waiting time is over and the
            // blockingMethod is still
            // running, I will reacquire the lock and finish.
            lock.wait(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

так что вы можете заменить

something.blockingMethod(input)

до

something.blockingMethod(input, 2000)

надеюсь, что это помогает.

вам понадобится автоматический выключатель реализация, как тот, который присутствует в failsafe проект на GitHub.

Я даю вам здесь полный код. Вместо метода, который я вызываю, вы можете использовать свой метод.

public class NewTimeout {

public String simpleMethod() {
    return "simple method";
}

public static void main(String[] args) {
    ExecutorService executor = Executors.newSingleThreadScheduledExecutor();
    Callable<Object> task = new Callable<Object>() {
        public Object call() throws InterruptedException {
            Thread.sleep(1100);
            return new NewTimeout().simpleMethod();
        }
    };
    Future<Object> future = executor.submit(task);
    try {
        Object result = future.get(1, TimeUnit.SECONDS); 
        System.out.println(result);
    } catch (TimeoutException ex) {
        System.out.println("Timeout............Timeout...........");
    } catch (InterruptedException e) {
        // handle the interrupts
    } catch (ExecutionException e) {
        // handle other exceptions
    } finally {
        executor.shutdown(); // may or may not desire this
    }
}

}

это действительно здорово, что люди пытаются реализовать это во многих отношениях. Но правда в том, что это невозможно.

большинство разработчиков попытались бы поместить блокирующий вызов в другой поток и иметь будущее или какой-то таймер. Но в Java нет способа остановить поток извне, не говоря уже о нескольких очень конкретных случаях, таких как поток.сон () и замок.lockInterruptibly() методы, которые явно обрабатывать прерывания потока.

Так что на самом деле у вас есть только 3 общих опции:

  1. поместите свой блокирующий вызов на Новый Поток, и если время истекает, вы просто двигаетесь дальше, оставляя этот поток висящим. В этом случае вы должны убедиться, что поток является потоком демона. Таким образом, поток не остановит ваше приложение от завершения.

  2. используйте неблокирующие API Java. Поэтому для сети, например, используйте NIO2 и используйте неблокирующие методы. Для чтения с консоли используйте сканер.hasNext() перед блокированием так далее.

  3. Если ваш блокирующий вызов не является IO, но ваша логика, то вы можете повторно проверить Thread.isInterrupted() чтобы проверить, был ли он прерван извне, и иметь другой вызов потока thread.interrupt() на блокирующем потоке

этот курс о параллелизме https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY

действительно проходит через эти основы, Если вы действительно хотите понять, как это работает в Java. Это на самом деле говорит о тех конкретных ограничениях и сценариях, и как идти о них в одной из лекций.

Я лично стараюсь программировать без использования блокировки вызовов как можно больше. Есть такие инструменты, как Vert.x например, что делает его очень легким и эффективным для выполнения операций ввода-вывода и без операций ввода-вывода асинхронно и не блокирующим способом.

надеюсь, это поможет