Có cách nào hay để gọi một phương thức chặn có thời gian chờ trong Java không? Tôi muốn có thể làm:
// call something.blockingMethod();
// if it hasn't come back within 2 seconds, forget it
nếu điều đó hợp lý.
Cảm ơn.
Có cách nào hay để gọi một phương thức chặn có thời gian chờ trong Java không? Tôi muốn có thể làm:
// call something.blockingMethod();
// if it hasn't come back within 2 seconds, forget it
nếu điều đó hợp lý.
Cảm ơn.
Câu trả lời:
Bạn có thể sử dụng 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
}
Nếu future.get
nó không quay trở lại trong 5 giây, nó ném a TimeoutException
. Thời gian chờ có thể được định cấu hình theo giây, phút, mili giây hoặc bất kỳ đơn vị nào có sẵn dưới dạng hằng số trong TimeUnit
.
Xem JavaDoc để biết thêm chi tiết.
BlockingMethodCallable
có tên là contructor của nó chấp nhận các tham số bạn muốn chuyển đến blockingMethod()
và lưu trữ chúng dưới dạng các biến thành viên (có thể là cuối cùng). Sau đó, bên trong call()
chuyển các tham số đó vào blockMethod()
.
future.cancel(true)
- Phương pháp hủy (boolean) trong các loại Future <Object> không áp dụng cho các đối số ()
Bạn có thể kết thúc cuộc gọi trong a FutureTask
và sử dụng phiên bản timeout của get ().
Xem http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/FutureTask.html
Xem thêm Guava's TimeLimiter sử dụng Executor đằng sau hậu trường.
Ngoài ra còn có một giải pháp AspectJ cho điều đó với thư viện jcabi-khía cạnh .
@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
}
Nó không thể ngắn gọn hơn, nhưng tất nhiên bạn phải phụ thuộc vào AspectJ và giới thiệu nó trong vòng đời xây dựng của bạn.
Có một bài viết giải thích thêm về nó: Giới hạn thời gian thực thi phương pháp Java
Thật tuyệt khi mọi người cố gắng thực hiện điều này theo nhiều cách. Nhưng sự thật là KHÔNG CÓ CÁCH NÀO.
Hầu hết các nhà phát triển sẽ cố gắng đặt cuộc gọi chặn trong một chuỗi khác và có một tương lai hoặc một số bộ đếm thời gian. NHƯNG không có cách nào trong Java để dừng một luồng bên ngoài, hãy nói đến một số trường hợp rất cụ thể như các phương thức Thread.sleep () và Lock.lockInterruptibly () xử lý rõ ràng việc ngắt luồng.
Vì vậy, thực sự bạn chỉ có 3 tùy chọn chung:
Đặt cuộc gọi chặn của bạn trên một chuỗi mới và nếu hết thời gian, bạn chỉ cần tiếp tục, để chuỗi đó bị treo. Trong trường hợp đó, bạn nên đảm bảo rằng luồng được đặt thành một luồng Daemon. Bằng cách này, chuỗi sẽ không ngăn ứng dụng của bạn kết thúc.
Sử dụng các API Java không chặn. Vì vậy, đối với mạng chẳng hạn, hãy sử dụng NIO2 và sử dụng các phương pháp không chặn. Để đọc từ bảng điều khiển, hãy sử dụng Scanner.hasNext () trước khi chặn, v.v.
Nếu cuộc gọi chặn của bạn không phải là IO mà là logic của bạn, thì bạn có thể liên tục kiểm tra Thread.isInterrupted()
để kiểm tra xem nó có bị gián đoạn bên ngoài hay không và có một cuộc gọi chuỗi khác thread.interrupt()
trên chuỗi chặn
Khóa học này về đồng thời https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY
thực sự đi qua những nguyên tắc cơ bản đó nếu bạn thực sự muốn hiểu cách nó hoạt động trong Java. Nó thực sự nói về những hạn chế và kịch bản cụ thể, và cách thực hiện chúng trong một trong các bài giảng.
Cá nhân tôi cố gắng lập trình mà không sử dụng cuộc gọi chặn càng nhiều càng tốt. Chẳng hạn, có những bộ công cụ như Vert.x giúp thực hiện IO và không thực hiện IO một cách đồng bộ và không bị chặn thực sự dễ dàng và hiệu quả.
Tôi hy vọng nó sẽ giúp
Thread thread = new Thread(new Runnable() {
public void run() {
something.blockingMethod();
}
});
thread.start();
thread.join(2000);
if (thread.isAlive()) {
thread.stop();
}
Lưu ý rằng điểm dừng đó không được dùng nữa, cách thay thế tốt hơn là đặt một số cờ boolean dễ bay hơi, bên trong blockMethod () hãy kiểm tra nó và thoát ra, như sau:
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)");
}
}
}
Thử cái này. Giải pháp đơn giản hơn. Đảm bảo rằng nếu khối không thực thi trong thời hạn. quá trình sẽ kết thúc và ném một ngoại lệ.
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;
}
}
thí dụ :
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
}
Tôi đang cung cấp cho bạn mã hoàn chỉnh ở đây. Thay cho phương thức tôi đang gọi, bạn có thể sử dụng phương thức của mình:
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
}
}
}
Giả sử blockingMethod
chỉ ngủ trong vài mili:
public void blockingMethod(Object input) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Giải pháp của tôi là sử dụng wait()
và synchronized
như thế này:
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();
}
}
}
Vì vậy, bạn có thể thay thế
something.blockingMethod(input)
đến
something.blockingMethod(input, 2000)
Hy vọng nó giúp.
Bạn cần triển khai bộ ngắt mạch giống như cách có trong dự án an toàn dự phòng trên GitHub.