Làm cách nào để gọi một số phương pháp chặn có thời gian chờ trong Java?


97

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.


1
Để tham khảo, hãy xem Java Concurrency in Practice của Brian Goetz trang 126 - 134, cụ thể là phần 6.3.7 "Đặt giới hạn thời gian cho các tác vụ"
brown.2179 4/1214

Câu trả lời:


151

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.getnó 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.


13
Phương thức chặn sẽ tiếp tục chạy ngay cả sau khi hết thời gian chờ, phải không?
Ivan Dubrov,

1
Điều đó phụ thuộc vào future.cancel. Tùy thuộc vào những gì phương pháp chặn đang thực hiện tại thời điểm đó, nó có thể chấm dứt hoặc không.
skaffman

4
làm cách nào để tôi có thể truyền tham số cho BlockMethod ()? Cảm ơn!
Robert A Henru

@RobertAHenru: Tạo một lớp mới BlockingMethodCallablecó 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().
Vite Falcon

1
cuối cùng nên làm 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ố ()
Noam Manos

9

Bạn có thể kết thúc cuộc gọi trong a FutureTaskvà 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


1
FutureTask tự nó không phải là không đồng bộ, phải không? Bản thân nó chỉ thực hiện mọi thứ một cách đồng bộ, bạn cần kết hợp nó với một Người thực thi để ví dụ như hành vi không đồng bộ.
skaffman 22-07-09


3

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


3

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:

  1. Đặ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.

  2. 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.

  3. 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


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

1

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
    }

1

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

0

Giả sử blockingMethodchỉ 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()synchronizednhư 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.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.