Як я можу викликати якийсь метод блокування з часом очікування в Java?


97

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

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

якщо це має сенс.

Дякую.


1
Як посилання, ознайомтеся з Java Concurrency на практиці, Брайан Гетц, с. 126 - 134, зокрема розділ 6.3.7 "Розміщення часових обмежень на завдання"
коричневий. 2117

Відповіді:


151

Ви можете скористатися виконавцем:

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 секунд, він видає a TimeoutException. Час очікування можна налаштувати в секундах, хвилинах, мілісекундах або будь-якій одиниці, доступній як константа в TimeUnit.

Докладніше див. У JavaDoc .


13
Метод блокування буде продовжувати працювати навіть після тайм-ауту, так?
Іван Дубров

1
Це залежить від future.cancel. Залежно від того, що робить метод блокування в той час, він може припинятися чи не припинятися.
skaffman

4
як я можу передати параметр blockingMethod ()? Дякую!
Robert A Henru

@RobertAHenru: Створіть новий клас із назвою BlockingMethodCallable, конструктор якого приймає параметри, до яких ви хочете передати, blockingMethod()і зберігає їх як змінні-члени (можливо, як остаточні). Потім всередині call()передайте ці параметри в blockMethod().
Vite Falcon

1
нарешті слід зробити future.cancel(true)- Метод cancel (логічний) у типі Future <Object> не застосовується до аргументів ()
Ноам Манос

9

Ви можете обернути виклик у FutureTaskта використати версію get () з часом очікування.

Див. Http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/FutureTask.html


1
FutureTask сам по собі не є асинхронним, правда? Сам по собі він просто робить речі синхронно, вам потрібно поєднати його з виконавцем, щоб виконати асинхронну поведінку.
skaffman


3

Існує також рішення 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


3

Насправді чудово, що люди намагаються реалізувати це у багатьох випадках. Але правда в тому, що ШЛЯХУ НЕМАЄ.

Більшість розробників намагаються помістити блокуючий виклик в інший потік і мати майбутнє або деякий таймер. АЛЕ в Java немає способу зупинити потік зовні, не кажучи вже про кілька дуже конкретних випадків, таких як методи Thread.sleep () та Lock.lockInterruptibly (), які явно обробляють переривання потоків.

Отже, насправді у вас є лише 3 загальних варіанти:

  1. Помістіть свій виклик блокування на новий потік, і якщо час закінчиться, ви просто рухаєтесь далі, залишаючи цю нитку завислою. У такому випадку слід переконатися, що для потоку встановлено потік Daemon. Таким чином потік не зупинить вашу програму від завершення.

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

  3. Якщо ваш виклик блокування - це не IO, а ваша логіка, тоді ви можете неодноразово Thread.isInterrupted()перевіряти, чи не було воно перервано зовні, і мати інший виклик потоку thread.interrupt()на потоці блокування

Цей курс про паралельність https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY

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

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

Сподіваюся, це допоможе


1
Thread thread = new Thread(new Runnable() {
    public void run() {
        something.blockingMethod();
    }
});
thread.start();
thread.join(2000);
if (thread.isAlive()) {
    thread.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)");
        }
    }
}

1

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

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
    }

1

Я даю вам тут повний код. Замість методу, який я викликаю, ви можете використовувати свій метод:

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

0

Припустимо, 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)

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


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