Java에서 시간 초과가있는 일부 차단 메서드를 어떻게 호출합니까?


97

Java에서 시간 초과로 차단 메서드를 호출하는 표준 좋은 방법이 있습니까? 나는 할 수 있기를 원한다 :

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

그게 말이된다면.

감사.


1
참고로 Brian Goetz의 Java Concurrency in Practice pp.
126-134

답변:


151

Executor를 사용할 수 있습니다.

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오초에 반환하지 않습니다, 그것은을 던졌습니다 TimeoutException. 시간 제한은 초, 분, 밀리 초 또는 상수로 사용 가능한 모든 단위로 구성 할 수 있습니다.TimeUnit .

자세한 내용은 JavaDoc 을 참조하십시오 .


13
차단 방법은 시간 초과 후에도 계속 실행됩니다.
Ivan Dubrov

1
그것은 future.cancel에 달려 있습니다. 차단 방법이 당시 수행중인 작업에 따라 종료되거나 종료되지 않을 수 있습니다.
skaffman

4
매개 변수를 blockingMethod ()에 어떻게 전달할 수 있습니까? 감사!
Robert A Henru 2011-08-22

@RobertAHenru : 생성자 BlockingMethodCallable가 전달하려는 매개 변수를 받아들이고 blockingMethod()구성원 변수로 저장 하는 새 클래스를 생성 합니다 (아마도 최종). 그런 다음 내부에서 call()해당 매개 변수를 blockMethod().
Vite Falcon 2013 년

1
마지막으로해야 future.cancel(true)-이 방법은 형의 미래에 (부울) 취소 <개체> 인수 () 적용 할 수 없습니다
노암 마 노스



3

jcabi-aspects 라이브러리 를 사용하는 AspectJ 솔루션도 있습니다.

@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 () 메서드와 같은 몇 가지 매우 특정한 경우는 말할 것도없고 외부 적으로 스레드를 중지 할 수있는 방법이 없습니다.

따라서 실제로는 세 가지 일반 옵션 만 있습니다.

  1. 차단 호출을 새 스레드에 놓고 시간이 만료되면 계속 진행하여 해당 스레드를 중단합니다. 이 경우 스레드가 데몬 스레드로 설정되어 있는지 확인해야합니다. 이렇게하면 스레드가 애플리케이션 종료를 중지하지 않습니다.

  2. 비 차단 Java API를 사용하십시오. 예를 들어 네트워크의 경우 NIO2를 사용하고 비 차단 방법을 사용하십시오. 콘솔에서 읽으려면 차단하기 전에 Scanner.hasNext () 등을 사용하십시오.

  3. 차단 호출이 IO가 아니라 논리 인 경우 반복적으로 Thread.isInterrupted()외부에서 중단되었는지 확인 thread.interrupt()하고 차단 스레드에서 다른 스레드 호출 을 수행 할 수 있습니다.

동시성에 대한이 과정 https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY

Java에서 작동하는 방식을 정말로 이해하고 싶다면 이러한 기본 사항을 설명합니다. 실제로 이러한 특정 제한 및 시나리오에 대해 설명하고 강의 중 하나에서 이에 대해 설명합니다.

저는 개인적으로 가능한 한 차단 호출을 사용하지 않고 프로그래밍하려고합니다. 예를 들어 Vert.x와 같은 툴킷은 IO를 수행하고 IO 작업을 비동기식으로 비 차단 방식으로 수행하는 것을 정말 쉽고 성능있게 만들어줍니다.

도움이 되길 바랍니다


1
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)");
        }
    }
}

1

이 시도. 더 간단한 솔루션. 시간 제한 내에 if 블록이 실행되지 않았 음을 보장합니다. 프로세스가 종료되고 예외가 발생합니다.

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();
        }
    }
}

그래서 u는 대체 할 수 있습니다

something.blockingMethod(input)

something.blockingMethod(input, 2000)

도움이되기를 바랍니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.