2010-08-05 30 views
13

Tôi đang cố gắng sử dụng một ThreadPoolExecutor để lên lịch các nhiệm vụ, nhưng gặp phải một số vấn đề với các chính sách của nó. Đây là hành vi đã nêu của nó:ThreadPoolExecutor policy

  1. Nếu ít hơn corePoolSize chủ đề đang chạy, Executor luôn thích thêm một chuỗi mới thay vì xếp hàng.
  2. Nếu corePoolSize hoặc nhiều chủ đề đang chạy, Executor luôn thích xếp hàng một yêu cầu hơn là thêm một chuỗi mới.
  3. Nếu yêu cầu không thể được xếp hàng đợi, một chuỗi mới sẽ được tạo trừ khi điều này vượt quá tối đaPoolSize, trong trường hợp đó, tác vụ sẽ bị từ chối.

Các hành vi tôi muốn là thế này:

  1. tương tự như trên
  2. Nếu có nhiều hơn corePoolSize nhưng ít hơn đề maximumPoolSize đang chạy, thích thêm một chủ đề mới hơn xếp hàng, và sử dụng một chủ đề nhàn rỗi hơn việc thêm một chuỗi mới.
  3. giống như trên

Về cơ bản tôi không muốn bất kỳ tác vụ nào bị từ chối; Tôi muốn họ được xếp hàng đợi trong một hàng đợi vô biên. Nhưng tôi muốn có tối đa chủ đềPoolPoolize. Nếu tôi sử dụng một hàng đợi không bị chặn, nó sẽ không bao giờ tạo ra các luồng sau khi nó truy cập coreSize. Nếu tôi sử dụng hàng đợi bị chặn, nó sẽ từ chối nhiệm vụ. Có cách nào để khắc phục điều này?

Điều tôi đang nghĩ bây giờ là chạy ThreadPoolExecutor trên SynchronousQueue, nhưng không cho phép tác vụ trực tiếp vào nó - thay vào đó hãy cho chúng vào một LinkedBlockingQueue không bị ràng buộc riêng biệt. Sau đó, một luồng khác được cung cấp từ LinkedBlockingQueue vào Executor, và nếu một bị từ chối, nó chỉ đơn giản là cố gắng một lần nữa cho đến khi nó không bị từ chối. Điều này có vẻ giống như một cơn đau và một chút của một hack, mặc dù - là có một cách sạch hơn để làm điều này?

Trả lời

1

Trường hợp sử dụng của bạn là phổ biến, hoàn toàn hợp pháp và không may khó hơn mong đợi. Để biết thông tin cơ bản, bạn có thể đọc this discussion và tìm con trỏ đến giải pháp (cũng được đề cập trong chuỗi) here. Shay của giải pháp hoạt động tốt.

Thông thường tôi sẽ cảnh giác với hàng đợi không bị ràng buộc; nó thường là tốt hơn để có kiểm soát dòng chảy đến rõ ràng mà làm giảm gracefully và điều chỉnh tỷ lệ của công việc hiện tại/còn lại để không áp đảo một trong hai nhà sản xuất hoặc người tiêu dùng.

1

Chỉ cần đặt corePoolsize = maximumPoolSize và sử dụng hàng đợi không bị chặn?

Trong danh sách điểm của bạn, 1 không bao gồm 2, vì corePoolSize sẽ luôn nhỏ hơn hoặc bằng maximumPoolSize.

Sửa

Hiện vẫn còn là một cái gì đó không tương thích giữa những gì bạn muốn và những gì TPE sẽ cung cấp cho bạn.

Nếu bạn có hàng đợi không bị chặn, maximumPoolSize bị bỏ qua, như bạn đã quan sát, không quá corePoolSize luồng sẽ được tạo và sử dụng.

Vì vậy, một lần nữa, nếu bạn chụp corePoolsize = maximumPoolSize với hàng đợi không có hàng, bạn có những gì bạn muốn, không?

+0

Rất tiếc, những gì tôi viết không chính xác những gì tôi muốn. Tôi đã chỉnh sửa bản gốc. –

+0

Thiết lập corePoolsize = maximumPoolSize thực sự là gần, nhưng tôi cũng sử dụng allowCoreThreadTimeOut (false) và prestartAllCoreThreads(). –

4

Nó có lẽ là không cần thiết để vi quản lý hồ bơi thread như được yêu cầu.

Hồ bơi chuỗi được lưu trong bộ nhớ cache sẽ sử dụng lại các chuỗi không hoạt động trong khi cũng cho phép các chuỗi đồng thời có khả năng không giới hạn.Điều này tất nhiên có thể dẫn đến hiệu suất runaway xuống cấp từ bối cảnh chuyển mạch trên không trong thời gian bùng nổ.

Executors.newCachedThreadPool(); 

Tùy chọn tốt hơn là đặt giới hạn cho tổng số chủ đề trong khi loại bỏ khái niệm đảm bảo luồng không sử dụng được sử dụng trước. Thay đổi cấu hình sẽ là:

corePoolSize = maximumPoolSize = N; 
allowCoreThreadTimeOut(true); 
setKeepAliveTime(aReasonableTimeDuration, TimeUnit.SECONDS); 

Lý do trên trường hợp này, nếu người thực thi có ít hơn corePoolSize chủ đề, không phải bận lắm. Nếu hệ thống không phải là rất bận rộn, sau đó có ít tác hại trong quay lên một sợi mới. Làm như vậy sẽ gây ra ThreadPoolExecutor của bạn để luôn tạo công nhân mới nếu số lượng nhân viên tối đa cho phép. Chỉ khi số lượng công nhân tối đa là "chạy" thì công nhân chờ đợi một cách nhàn rỗi cho các nhiệm vụ được giao nhiệm vụ. Nếu một công nhân chờ đợi aReasonableTimeDuration mà không có tác vụ, thì nó sẽ được phép chấm dứt. Sử dụng giới hạn hợp lý cho kích thước hồ bơi (sau khi tất cả, chỉ có rất nhiều CPU) và thời gian chờ hợp lý lớn (để giữ cho các chủ đề kết thúc không cần thiết), các lợi ích mong muốn có thể sẽ được nhìn thấy.

Tùy chọn cuối cùng là hackish. Về cơ bản, ThreadPoolExecutor sử dụng nội bộ BlockingQueue.offer để xác định xem hàng đợi có dung lượng hay không. Triển khai tùy chỉnh BlockingQueue luôn có thể từ chối nỗ lực offer. Khi ThreadPoolExecutor không thể offer một nhiệm vụ vào hàng đợi, nó sẽ cố gắng để làm cho một công nhân mới. Nếu không thể tạo nhân viên mới, RejectedExecutionHandler sẽ được gọi. Tại thời điểm đó, tùy chỉnh RejectedExecutionHandler có thể bắt buộc put vào tùy chỉnh BlockingQueue.

/** Hackish BlockingQueue Implementation tightly coupled to ThreadPoolexecutor implementation details. */ 
class ThreadPoolHackyBlockingQueue<T> implements BlockingQueue<T>, RejectedExecutionHandler { 
    BlockingQueue<T> delegate; 

    public boolean offer(T item) { 
     return false; 
    } 

    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { 
     delegate.put(r); 
    } 

    //.... delegate methods 
} 
Các vấn đề liên quan