2010-04-28 36 views
26

Có cách nào để giết một chuỗi con sau một số giới hạn thời gian cụ thể trong Java không? Chỉnh sửa: Ngoài ra thread cụ thể này có thể bị chặn trong trường hợp xấu nhất của nó (Thread được sử dụng để chờ sửa đổi tập tin và chặn cho đến khi sự kiện này xảy ra), vì vậy im không chắc chắn rằng ngắt() sẽ thành công?Giết chủ đề sau một số giới hạn thời gian được chỉ định trong Java

+0

bạn đang sử dụng gì để chờ đợi sửa đổi tập tin? Object.wait() hoặc cái gì khác? – mdma

+0

Thực ra tôi đang sử dụng đồng hồ bấm giờ để tìm kiếm các sửa đổi tệp và đặc biệt 'watchService.take()' sẽ chặn cho đến khi một tệp đã được tạo/xóa. – Traker

Trả lời

33

Sử dụng ExecutorService để thực hiện Runnable, kiểm tra các phương pháp trong đó bạn có thể chỉ định thời gian chờ. Ví dụ.

ExecutorService executor = Executors.newSingleThreadExecutor(); 
executor.invokeAll(Arrays.asList(new Task()), 10, TimeUnit.MINUTES); // Timeout of 10 minutes. 
executor.shutdown(); 

Đây Task thực hiện khóa học Runnable.

+0

Các đề xuất tốt, nhưng ExecutorService có thể hết thời gian chờ ngay cả khi nó bị chặn không? – Traker

+1

Nó sẽ làm gián đoạn luồng, vâng. Nếu bạn muốn sử dụng nó, hãy sử dụng 'ExecutorService # awaitTermination()', cũng xem ví dụ trong phần giới thiệu của Javadoc. – BalusC

+14

IMHO, 'invokeAll' là quá mức cần thiết. Chỉ cần sử dụng 'executor.submit (new Task()). Get (10, TimeUnit.MINUTES);' –

5

Tại sao không interrupt() sau một thời gian cụ thể? Chủ đề sinh ra của bạn sẽ phải có khả năng xử lý một số InterruptedException đúng cách.

Xem bài viết này (http://www.javaspecialists.eu/archive/Issue056.html) để biết thêm thông tin về cách tắt chủ đề một cách rõ ràng.

Xem thêm Khung công tác/Tương lai, cung cấp các phương pháp hữu ích để thu thập kết quả và/hoặc chấm dứt chuỗi trong giới hạn thời gian cụ thể.

+1

Chuỗi chỉ cần trả lời 'InterruptedException' nếu nó thực hiện cuộc gọi có thể ném nó. Nếu nó đang quay trong một vòng lặp, nó phải kiểm tra cờ ngắt riêng của nó bằng cách sử dụng 'Thread.isInterrupted()' (hoặc chỉ 'Thread.interrupted()', ngụ ý luồng hiện tại). –

0

Giết chủ đề thường là một ý tưởng tồi vì các lý do liên quan đến tài liệu API cho Thread.

Nếu bạn chết khi chết, hãy sử dụng một quy trình hoàn toàn mới.

Nếu không, điều bình thường là phải có phiếu thăm dò chủ đề System.nanoTime, thăm dò ý kiến ​​(có thể volatile) gắn cờ, xếp hàng "thuốc độc" hoặc thứ gì đó có tính chất đó.

+0

Giết chủ đề có thể rất quan trọng, nếu bạn không sở hữu mã mà luồng này chạy, và bạn cần đảm bảo rằng anh ta đã chết. Đôi khi bạn phải có cả hai mã chạy trong cùng một địa chỉ ảo. –

+0

@Elazar Nó gần như chắc chắn quan trọng hơn là quá trình này vẫn ổn định. –

0

Quyền của Brian, làm gián đoạn việc này an toàn hơn là "dừng" chuỗi đó.
Điều gì sẽ xảy ra nếu chủ đề đang khóa trên một đối tượng giữa sửa đổi và đột nhiên bị dừng lại (điều này làm cho khóa được giải phóng)? Bạn nhận được kết quả lạ.

4

Không trực tiếp; Tôi nghĩ cách đơn giản nhất là tham gia() trên chuỗi đó với giới hạn thời gian đó và ngắt chuỗi nếu nó không được thực hiện vào lúc kết thúc.

Vì vậy,

Thread t = ... 
t.join(timelimit); 
if (t.isAlive) t.interrupt(); 

Thông báo tôi đã sử dụng ngắt thay vì thực sự giết chết nó, nó là an toàn hơn nhiều. Tôi cũng khuyên bạn nên sử dụng các nhà thực thi thay vì trực tiếp thao tác các chủ đề.

+0

Điều này sẽ không hoạt động tốt nếu luồng khác lấy tất cả CPU, do đó kết nối chỉ là _started_ sau một độ trễ quá lớn. – mafu

+0

Tôi nghĩ rằng nên đọc 'if (t.isAlive())' – PHPirate

0

Không sử dụng destroy() vì điều đó không thực hiện bất kỳ thao tác dọn dẹp nào.

Cách đơn giản nhất là sử dụng join(), như

try { 
    thread.join(); 
} catch (InterruptedException e) {//log exception...} 

Bạn có thể sử dụng một ExecutorService. Điều đó sẽ làm cho rất nhiều ý nghĩa nếu bạn có một số chủ đề chạy đồng thời. Nếu bạn có nhu cầu sinh ra các luồng mới trong khi các luồng khác đang chạy, bạn có thể kết hợp nó với một số BlockingQueue.

A ThreadPoolExecutor (an ExecutorService -thực hiện) có thể tham số BlockingQueue làm đối số và bạn có thể chỉ cần thêm chuỗi mới vào hàng đợi. Khi bạn hoàn thành, bạn chỉ cần chấm dứt số ThreadPoolExecutor.

private BlockingQueue<Runnable> queue; 
... 
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, new Long(1000), 
       TimeUnit.MILLISECONDS, this.queue); 

Bạn có thể đếm tổng số chủ đề được thêm vào hàng đợi. Khi bạn nghĩ rằng bạn đã hoàn thành (hàng đợi trống rỗng, có lẽ?) Chỉ cần so sánh số này với số

if (issuedThreads == pool.getCompletedTaskCount()) { 
     pool.shutdown(); 
    } 

Nếu hai kết thúc, bạn đã hoàn tất. Một cách khác để chấm dứt các hồ bơi là để chờ đợi một giây trong một vòng lặp:

try { 
     while (!this.pool.awaitTermination(1000, TimeUnit.MILLISECONDS)); 
} catch (InterruptedException e) {//log exception...} 
1

Bạn có thể sử dụng AOP và một @Timeable chú thích cho phương pháp của bạn từ jcabi-aspects (Tôi là một nhà phát triển):

@Timeable(limit = 1, unit = TimeUnit.SECONDS) 
String load(String resource) { 
    // do something time consuming 
} 

Khi đạt đến giới hạn thời gian, chuỗi của bạn sẽ nhận được interrupted() cờ được đặt thành true và đó là công việc của bạn để xử lý tình huống này một cách chính xác và ngừng thực thi. Thông thường nó được thực hiện bởi Thread.sleep(..).

0

Một số thay đổi hữu ích đã được giới thiệu như là một phần của JEP 266 trong CompletableFuture kể từ Java 9. Sử dụng orTimeout phương pháp, bây giờ, chúng ta có thể viết nó thích:

CompletableFuture.runAsync(thread::run) 
    .orTimeout(30, TimeUnit.SECONDS) 
    .exceptionally(throwable -> { 
     log.error("An error occurred", throwable); 
     return null; 
    }); 

Trong Java 8, không may, bạn nên sử dụng thêm một số mã. Dưới đây là một ví dụ về việc sử dụng mô hình đoàn với sự giúp đỡ của Lombok:

import com.google.common.util.concurrent.ThreadFactoryBuilder; 
import java.time.Duration; 
import java.util.concurrent.CompletableFuture; 
import java.util.concurrent.Executors; 
import static java.util.concurrent.TimeUnit.MILLISECONDS; 
import java.util.concurrent.TimeoutException; 
import static lombok.AccessLevel.PRIVATE; 
import lombok.AllArgsConstructor; 
import lombok.experimental.Delegate; 

@AllArgsConstructor(access = PRIVATE) 
public class TimeoutableCompletableFuture<T> extends CompletableFuture<T> { 

    public static TimeoutableCompletableFuture<Void> runAsync(
      Runnable runnable) { 
     return new TimeoutableCompletableFuture<>(
       CompletableFuture.runAsync(runnable)); 
    } 

    @Delegate 
    private final CompletableFuture<T> baseFuture; 

    public TimeoutableCompletableFuture<T> orTimeout(Duration duration) { 
     final CompletableFuture<T> otherFuture = new CompletableFuture<>(); 
     Executors.newScheduledThreadPool(
       1, 
       new ThreadFactoryBuilder() 
       .setDaemon(true) 
       .setNameFormat("timeoutable-%d") 
       .build()) 
       .schedule(() -> { 
        TimeoutException ex = new TimeoutException(
          "Timeout after " + duration); 
        return otherFuture.completeExceptionally(ex); 
       }, duration.toMillis(), MILLISECONDS); 

     return new TimeoutableCompletableFuture<>(
       baseFuture.applyToEither(otherFuture, a -> a)); 
    } 
} 

Tất nhiên, các mã trên một cách dễ dàng có thể được viết lại như chỉ là một phương thức tĩnh:

public static CompletableFuture<Void> runAsyncOrTimeout(
     Runnable runnable, long timeout, TimeUnit unit) { 

    CompletableFuture<Void> other = new CompletableFuture<>(); 
    Executors.newScheduledThreadPool(
      1, 
      new ThreadFactoryBuilder() 
      .setDaemon(true) 
      .setNameFormat("timeoutafter-%d") 
      .build()) 
      .schedule(() -> { 
       TimeoutException ex = new TimeoutException(
         "Timeout after " + timeout); 
       return other.completeExceptionally(ex); 
      }, timeout, unit); 
    return CompletableFuture.runAsync(runnable).applyToEither(other, a -> a); 
} 
Các vấn đề liên quan