2013-02-05 35 views
5

Tôi cố gắng làm việc với các loại FutureTask, Future, Runnable, CallableExecutorService của Java.Tính tương thích của FutureTask của Java

Phương pháp hay nhất để soạn những khối xây dựng đó là gì?

Cho rằng tôi có nhiều FutureTask giây và tôi muốn thực thi chúng theo thứ tự.

ofcourse Tôi có thể tạo một FutureTask khác đang gửi/chờ kết quả cho mỗi thao tác theo trình tự, nhưng tôi muốn tránh chặn cuộc gọi.

Một tùy chọn khác sẽ là để những nhiệm vụ phụ gọi lại cuộc gọi lại khi hoàn thành và lên lịch công việc tiếp theo trong cuộc gọi lại. Nhưng đi con đường đó, làm thế nào để tôi tạo ra một đối tượng FutureTask bên ngoài thích hợp mà cũng xử lý các ngoại lệ trong subtask mà không tạo ra nhiều bản mẫu đó?

Tôi có bỏ sót điều gì ở đây không?

Trả lời

5

Điều rất quan trọng, mặc dù thường không được mô tả trong hướng dẫn:

Runnables to be executed on an ExecutorService should not block. Điều này là do mỗi khóa chặn tắt một chuỗi làm việc và nếu ExecutorService có số lượng chuỗi làm việc hạn chế, có nguy cơ rơi vào bế tắc (sự đói), và nếu ExecutorService có số luồng làm việc không giới hạn, thì có nguy cơ hết bộ nhớ. Việc chặn các thao tác trong các nhiệm vụ chỉ đơn giản là tiêu diệt tất cả các ưu điểm của ExecutorService, vì vậy chỉ sử dụng các hoạt động chặn trên các luồng thông thường.

FutureTask.get() đang chặn hoạt động, vì vậy có thể được sử dụng cho các chuỗi thông thường chứ không phải từ tác vụ ExecutorService. Nghĩa là, nó không thể phục vụ như một khối xây dựng, nhưng chỉ để cung cấp kết quả thực hiện cho luồng chủ.

Cách tiếp cận đúng để xây dựng thực thi từ nhiệm vụ là bắt đầu nhiệm vụ tiếp theo khi tất cả dữ liệu đầu vào cho tác vụ tiếp theo đã sẵn sàng để nhiệm vụ không phải chờ đợi dữ liệu đầu vào. Vì vậy, bạn cần một loại cổng mà lưu trữ kết quả trung gian và bắt đầu nhiệm vụ mới khi tất cả các đối số đã đến. Do đó các tác vụ không bận tâm một cách rõ ràng để bắt đầu các tác vụ khác. Vì vậy, một cổng, trong đó bao gồm các ổ cắm đầu vào cho các đối số và một Runnable để tính toán chúng, có thể được coi là một khối xây dựng đúng cho tính toán trên ExcutorServices.

Phương pháp này được gọi là dataflow hoặc quy trình làm việc (nếu cổng không thể được tạo động).

Các khung diễn viên như Akka sử dụng cách tiếp cận này nhưng bị giới hạn trong thực tế là một diễn viên là một cổng có ổ cắm đầu vào đơn.

Tôi đã viết thư viện dữ liệu đúng thực được xuất bản tại https://github.com/rfqu/df4j.

+0

Cảm ơn lời giải thích đó. Về cơ bản nó chỉ ra vấn đề của tôi rất tốt. Vấn đề là nếu bạn có một số loại nhiệm vụ mà bạn muốn sử dụng lại, thì logic phải làm gì sau khi không thể được đưa vào nhiệm vụ đó và giao diện Tương lai thiếu các cuộc gọi lại không đồng bộ. Gần đây tôi đã xem https://code.google.com/p/guava-libraries/wiki/ListenableFutureExplained cũng cố gắng giải quyết vấn đề này. Nhưng tôi thường muốn tìm hiểu làm thế nào nó có nghĩa là để làm việc, và về cơ bản, nếu tôi không cho rằng họ không nghĩ về thiết kế này các giao diện, tôi vẫn không nhận được nó. – Nappy

+1

Cách tốt nhất để cảm ơn là upvote câu trả lời. Df4j có nhiều loại khối xây dựng, bao gồm 'CallbackPromise' gần với' ForwardingCheckedFuture' của ổi. Nếu bạn thấy rằng một số loại khối xây dựng khác có giá trị bao gồm trong thư viện, chỉ cần gửi một sự cố tại https://github.com/rfqu/df4j/issues –

+0

Có vẻ như bạn đã hiểu sai, Akka dataflow không sử dụng diễn viên: http://doc.akka.io/docs/akka/2.1.0/scala/dataflow.html, Akka Futures (hiện có trong Thư viện Chuẩn Scala, không sử dụng Diễn viên: http://doc.akka.io/ docs/akka/2.1.0/scala/futures.html) –

0

Tôi đã cố gắng thực hiện điều gì đó tương tự với ScheduledFuture, cố gắng gây ra sự chậm trễ trước khi mọi thứ được hiển thị cho người dùng. Đây là những gì tôi đưa ra, chỉ cần sử dụng cùng một ScheduledFuture cho tất cả các 'sự chậm trễ' của bạn. Mã này là:

public static final ScheduledExecutorService scheduler = Executors 
     .newScheduledThreadPool(1); 
    public ScheduledFuture delay = null; 

    delay = scheduler.schedule(new Runnable() { 
    @Override 
    public void run() { 
    //do something 
    } 
    }, 1000, TimeUnit.MILLISECONDS); 

    delay = scheduler.schedule(new Runnable() { 
    @Override 
    public void run() { 
    //do something else 
    } 
    }, 2000, TimeUnit.MILLISECONDS); 

Hope this helps Andy

+0

Lưu ý rằng ScheduledFuture có thể bị hủy trước khi thực hiện Runnable của nó. Trong ví dụ này, bạn sẽ không thể hủy công việc đầu tiên (trừ việc lấy nó từ hàng đợi của người lên lịch). –

+0

Cảm ơn bạn đã sửa. Chỉ cần quan tâm, ví dụ trên có hoạt động như dự định nếu tôi có hai delay1 và delay2 của ScheduledFuture không? –

+1

Với cuộc gọi đầu tiên, trình lên lịch sẽ bao bọc runnable vào một tương lai, đặt nó vào hàng đợi nhiệm vụ của nó và trả nó về chuỗi cuộc gọi. Nó không quan trọng với lịch trình, nếu bạn thực sự đặt nó vào một biến hay không. Vì vậy, giữ cho tương lai chỉ để hủy bỏ sau này hoặc truy vấn thời gian để thực hiện với getDelay. –

0

Cách tiếp cận thông thường là:

  • Quyết định về ExecutorService (loại, có bao nhiêu chủ đề).
  • Quyết định hàng đợi công việc (khoảng thời gian có thể không bị chặn).

Nếu bạn có một số mã bên ngoài chờ kết quả nhiệm vụ: * Gửi nhiệm vụ làm Cuộc gọi (điều này không chặn miễn là bạn không hết hàng). * Cuộc gọi nhận được trong tương lai.

Nếu bạn muốn có một số hành động được thực hiện tự động sau khi nhiệm vụ được hoàn thành:

  • Bạn có thể gửi như Callables hoặc Runnables.
  • Chỉ cần thêm rằng bạn cần phải làm ở cuối là mã cuối cùng bên trong tác vụ. Sử dụng Activity.runOnUIThread những hành động cuối cùng này cần phải sửa đổi GUI.

Thông thường, bạn không nên chủ động kiểm tra khi nào bạn có thể gửi thêm một tác vụ hoặc lên lịch gọi lại để chỉ gửi chúng. Hàng đợi chuỗi (chặn, nếu được ưa thích) sẽ xử lý điều này cho bạn.

Các vấn đề liên quan