2012-03-01 30 views
15

Tôi đã nhìn xung quanh nhưng chưa tìm thấy câu trả lời nên tôi muốn xác nhận điều này một cách chắc chắn.Java Thread Pools/Executor Service và wait() s - điều gì sẽ xảy ra với chuỗi chủ đề và nhiệm vụ?

Nói rằng tôi có một cố định kích thước hồ bơi thread - ExecutorService pool = Executors.newFixedThreadPool(5);

Và tôi có một số mã:

pool.execute(new Runnable(){ 
    try{ 
     Object waitForMe = doSomethingAndGetObjectToWaitFor(); 
     waitForMe.wait(); 
     doSomethingElse(); 
    }catch(Exception e){ throw new RunTimeException(e) } 

}); 

phép giả định rằng các mã trên được gọi là một vài 100 lần. Chỉ có 5 chủ đề trong hồ bơi (vì vậy chỉ có 5 câu trên được phát hành tại một thời điểm). Cũng giả sử rằng wait() là trên một đối tượng đang thực hiện một số cuộc gọi I/O tới một bên thối và chờ đợi một cuộc gọi lại khi thao tác hoàn tất, do đó sẽ mất một lúc để hoàn thành một cách tự nhiên.

Bây giờ câu hỏi của tôi là hành vi khi một trong các tác vụ này đạt đến một wait(), tác vụ có chuyển sang chế độ ngủ hay không và sau đó chuỗi từ nhóm chủ đề lấy một hàng đợi khác và bắt đầu chạy nó?

Nếu tác vụ đang chờ chuyển sang chế độ ngủ, điều gì sẽ xảy ra khi điện thoại nhận được số notify() và đánh thức? Chủ đề có quay trở lại hàng đợi (ở mặt trước hoặc sau) cho nhóm chủ đề và đợi cho đến khi một trong 5 chủ đề có thể tiếp tục thực hiện nó (tức là gọi doSomethingelse()) không? Hoặc hiện các chủ đề được thực hiện nó cũng đi ngủ nghĩa là một trong 5 chủ đề thi hành ngồi chờ đợi với nhiệm vụ (đây là những gì tôi giả định)? Hoặc không thực hiện các chủ đề nhận một nhiệm vụ khác và chỉ đơn giản là bị gián đoạn khi nhiệm vụ đầu tiên trở về từ wait()?

Trả lời

16

wait() là một hoạt động chặn:

Nguyên nhân xử lí hiện tại để chờ cho đến khi thread khác gọi phương thức notify() hoặc notifyAll()

Điều này có nghĩa rằng các chủ đề trong hồ bơi sẽ đợi, nhưng từ bên ngoài nó trông giống như nhiệm vụ hiện tại mất rất nhiều thời gian để hoàn thành. Điều này cũng có nghĩa là nếu 5 nhiệm vụ được thực hiện và tất cả chúng đều là wait(), thì Executor không thể xử lý các tác vụ còn lại, ekhem, chờ trong hàng đợi.

Đúng, bản thân bộ thực thi chuyển sang chế độ ngủ cho phép các chủ đề khác chuyển đổi và tiêu thụ CPU (để bạn có thể có hàng trăm chủ đề cùng một lúc và hệ thống của bạn vẫn đáp ứng) nhưng vẫn là chủ đề "không sử dụng được" và bị chặn.

Một tính năng thú vị khác là làm gián đoạn - nếu chuỗi chờ đợi gì đó hoặc ngủ bạn có thể làm gián đoạn. Lưu ý rằng cả hai số wait()Thread.sleep() khai báo InterruptedException. Với ExecutorService bạn có thể tận dụng điều này bằng cách chỉ cần gọi: future.cancel() (future là đối tượng bạn nhận được khi trả lại tác vụ cho ExecutorService).

Cuối cùng tôi nghĩ bạn nên thiết kế lại giải pháp của mình. Thay vì chủ động chờ đợi một hệ thống bên ngoài để hoàn thành, cung cấp một API với callbacks:

pool.execute(new Runnable(){ 
    try{ 
     doSomethingAndCallMeBackWhenItsDone(new Callback() { 
      public void done() { 
       doSomethingElse(); 
      } 
     }); 
    }catch(Exception e){ throw new RunTimeException(e) } 

}); 

Bằng cách này API hệ thống bên ngoài chỉ đơn giản là sẽ thông báo cho bạn khi kết quả đã sẵn sàng và bạn sẽ không phải chờ đợi và chặn ExecutorService .Cuối cùng, nếu doSomethingElse() mất rất nhiều thời gian, bạn có thể thậm chí quyết định để sắp xếp nó cũng như thay vì sử dụng bên ngoài của bên thứ ba I/O Chủ đề:

pool.execute(new Runnable(){ 
    try{ 
     doSomethingAndCallMeBackWhenItIsDone(new Callback() { 
      public void done() { 
       pool.submit(new Callbale<Void>() { 
        public Void call() { 
         doSomethingElse(); 
        } 
       } 
      } 
     }); 
    }catch(Exception e){ throw new RunTimeException(e) } 

}); 

UPDATE: bạn đang yêu cầu phải làm gì về timeouts? Đây là ý tưởng của tôi:

pool.execute(new Runnable(){ 
    try{ 
     doSomethingAndCallMeBackWhenItsDone(new Callback() { 
      public void done() { 
       doSomethingElse(); 
      } 
      public void timeout() { 
       //opps! 
      } 
     }); 
    }catch(Exception e){ throw new RunTimeException(e) } 

}); 

Tôi đoán bạn có thể thực hiện thời gian chờ ở phía bên thứ ba và nếu timeout xảy ra ở đó, chỉ cần gọi timeout() phương pháp.

+1

Cảm ơn câu trả lời tuyệt vời! Với tùy chọn gọi lại, những gì sẽ là cách tốt nhất để đi về thiết lập một thời gian chờ (nói muốn chờ đợi x giây cho một cuộc gọi lại nếu không có gì ném một lỗi). Cách duy nhất tôi có thể nghĩ là ghi lại các hệ thống hiện thời và giữ nó trong danh sách và có một luồng khác theo dõi danh sách các cuộc gọi đã vượt quá thời gian hiện tại và kích hoạt một lỗi. Tôi cần phải tìm một cuốn sách tốt mà đề với concurrency, callbacks vv Cảm ơn một lần nữa! – NightWolf

+1

@NightWolf: wrt to timeouts xem câu trả lời cập nhật của tôi. Khi nói đến một cuốn sách hay, [Java Concurrency in Practice] (http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601) là điều bắt buộc. –

+0

Cảm ơn heaps cho bản cập nhật và liên kết sách, ý tưởng hay. Đáng buồn là tôi không có quyền kiểm soát bên thứ ba. – NightWolf

1

wait() không thể biết gì về bể bơi. Và hồ bơi chủ đề không thể biết gì về số wait(). Vì vậy, họ không thể tương tác dù sao đi nữa.

Chúng hoạt động như bình thường - wait() chỉ là một hoạt động chặn hoạt động lâu dài, nhóm chủ đề chỉ là một hàng đợi các runnables chạy trên nhóm chủ đề hạn chế.

0

Tôi muốn nhận xét về câu trả lời của Tomasz nhưng danh tiếng của tôi không cho phép (chưa), xin lỗi.

Tôi biết câu hỏi cũ, nhưng đối với những người vẫn đọc trang này, hãy xem Tương lai và đặc biệt là số ListenableFuture của ổi cho phép bạn đăng ký cuộc gọi lại và chuỗi tương lai cùng với mục đích không chặn thread của bạn (và do đó miễn phí các chủ đề trở lại hồ bơi cho một cái gì đó khác để sử dụng nó).

0

Tất cả 5 luồng sẽ bị chặn và ứng dụng sẽ ở trạng thái không hiệu quả.

Thêm vào Tomasz câu trả lời, tôi muốn thực hiện thời gian ra cơ chế như sau.

  Future<Long> futureResult = service.execute(myCallable); 
      Long result = null; 
      try{ 
       result = futureResult.get(5000, TimeUnit.MILLISECONDS); 
      }catch(TimeoutException e){ 
       System.out.println("Time out after 5 seconds"); 
       futureResult.cancel(true); 
      }catch(InterruptedException ie){ 
       System.out.println("Error: Interrupted"); 
      }catch(ExecutionException ee){ 
       System.out.println("Error: Execution interrupted"); 
      } 

Ngoài TimeoutException, bạn có thể hủy trong tương lai trong InterruptedException & ExecutionException. Nếu bạn sử dụng submit() thay vì thực hiện(), InterruptedException & ExecutionException sẽ được nuốt trong khung chính nó.

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