60

Vì vậy, tôi có một chương trình sinh ra các luồng (~ 5-150) thực hiện một loạt các nhiệm vụ. Ban đầu tôi đã sử dụng FixedThreadPoolthis similar question đề nghị chúng phù hợp hơn với các công việc lâu hơn và với kiến ​​thức rất hạn chế về đa luồng, tôi coi cuộc sống trung bình của chủ đề (vài phút) "long lived".FixedThreadPool vs CachedThreadPool: ít hơn của hai tệ nạn

Tuy nhiên, gần đây tôi đã thêm khả năng tạo ra các chuỗi bổ sung và làm như vậy sẽ đưa tôi vượt quá giới hạn chuỗi mà tôi đã đặt. Trong trường hợp này, nó sẽ là tốt hơn để đoán và tăng số lượng chủ đề tôi có thể cho phép hoặc chuyển sang một CachedThreadPool vì vậy tôi không có chủ đề lãng phí?

Đang cố gắng cả hai ra sơ, có không vẻ là một sự khác biệt vì vậy tôi có khuynh hướng đi với CachedThreadPool chỉ để tránh lãng phí. Tuy nhiên, tuổi thọ của các chủ đề có nghĩa là tôi nên chọn một FixedThreadPool và chỉ xử lý các chủ đề không được sử dụng? This question làm cho nó có vẻ như những chủ đề phụ không lãng phí nhưng tôi sẽ đánh giá cao làm rõ.

Trả lời

81

Một CachedThreadPool là chính xác những gì bạn nên sử dụng cho tình huống của bạn vì không có hậu quả tiêu cực để sử dụng một cho các chủ đề chạy dài. Nhận xét trong tài liệu java về CachedThreadPools thích hợp cho các tác vụ ngắn chỉ đơn thuần gợi ý rằng chúng đặc biệt thích hợp cho các trường hợp như vậy, không phải là chúng không thể hoặc không được sử dụng cho các tác vụ liên quan đến các tác vụ chạy dài.

Để xây dựng thêm, Executors.newCachedThreadPoolExecutors.newFixedThreadPool đều được hỗ trợ bởi việc triển khai cùng một nhóm luồng (ít nhất là trong JDK mở) chỉ với các tham số khác nhau. Sự khác biệt chỉ là chủ đề của họ tối thiểu, tối đa, thời gian giết thread và loại hàng đợi.

public static ExecutorService newFixedThreadPool(int nThreads) { 
    return new ThreadPoolExecutor(nThreads, nThreads, 
            0L, TimeUnit.MILLISECONDS, 
            new LinkedBlockingQueue<Runnable>()); 
} 

public static ExecutorService newCachedThreadPool() { 
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 
           60L, TimeUnit.SECONDS, 
           new SynchronousQueue<Runnable>()); 
} 

Một FixedThreadPool không có lợi thế của mình khi bạn làm trong thực tế muốn làm việc với một số cố định của chủ đề, từ đó bạn có thể gửi bất kỳ số lượng nhiệm vụ cho các dịch vụ thi hành di chúc trong khi biết rằng số lượng đề sẽ duy trì ở mức bạn đã chỉ định. Nếu bạn rõ ràng muốn tăng số lượng chủ đề, thì đây không phải là lựa chọn thích hợp.

Tuy nhiên, điều này có nghĩa là một vấn đề mà bạn có thể gặp trong CachedThreadPool liên quan đến việc giới hạn số chuỗi đang chạy đồng thời. CachedThreadPool sẽ không giới hạn chúng cho bạn, vì vậy bạn có thể cần phải viết mã của riêng bạn để đảm bảo rằng bạn không chạy quá nhiều luồng. Điều này thực sự phụ thuộc vào thiết kế ứng dụng của bạn và cách các tác vụ được gửi đến dịch vụ thi hành.

19

Cả hai FixedThreadPoolCachedThreadPool là các tệ nạn trong các ứng dụng được tải cao.

CachedThreadPool là nguy hiểm hơn FixedThreadPool

Nếu ứng dụng của bạn được nạp cao & nhu cầu độ trễ thấp, tốt hơn để thoát khỏi cả hai lựa chọn do xuống dưới nhược điểm

  1. thiên nhiên bị chặn của hàng đợi công việc: Nó có thể gây ra bộ nhớ hoặc độ trễ cao
  2. Chủ đề đang chạy dài sẽ gây ra CachedThreadPool để vượt ra ngoài tầm kiểm soát trên Tạo chủ đề

Vì bạn biết rằng cả hai đều là tệ nạn, ít ác hơn không làm tốt. Ưu tiên ThreadPoolExecutor, cung cấp khả năng kiểm soát chi tiết trên nhiều thông số.

  1. Đặt hàng đợi nhiệm vụ như hàng đợi giáp để có thể kiểm soát tốt hơn
  2. Có đúng RejectionHandler - RejectionHandler riêng của bạn hoặc xử lý mặc định được cung cấp bởi JDK
  3. Nếu bạn có một cái gì đó để làm trên trước/sau khi hoàn thành nhiệm vụ, ghi đè beforeExecute(Thread, Runnable)afterExecute(Runnable, Throwable)
  4. override ThreadFactory nếu chủ đề tùy biến được yêu cầu
  5. kích thước
  6. kiểm soát chủ đề hồ bơi tự động tại thời gian chạy (liên quan SE câu hỏi: Dynamic Thread Pool)
5

Vì vậy, tôi có một chương trình sinh ra các luồng (~ 5-150) thực hiện một loạt tác vụ.

Bạn có chắc bạn hiểu cách xử lý thực sự của hệ điều hành và phần cứng bạn chọn? Làm thế nào Java ánh xạ chủ đề cho các chủ đề hệ điều hành, làm thế nào mà bản đồ chủ đề để CPU chủ đề vv? Tôi hỏi vì tạo 150 chủ đề trong ONE JRE chỉ có ý nghĩa nếu bạn có lõi CPU/chủ đề lớn bên dưới, mà rất có thể không phải là trường hợp. Tùy thuộc vào hệ điều hành và RAM đang được sử dụng, việc tạo nhiều hơn n luồng thậm chí có thể dẫn đến kết quả JRE của bạn bị chấm dứt do lỗi OOM. Vì vậy, bạn nên thực sự phân biệt giữa các chủ đề và công việc để làm bởi những chủ đề đó, có bao nhiêu công việc bạn thậm chí có thể xử lý vv.

Và đó là vấn đề với CachedThreadPool: Nó không có ý nghĩa để xếp hàng hoạt động lâu dài trong các chủ đề thực sự không thể chạy vì bạn chỉ có 2 lõi CPU có thể xử lý các luồng đó. Nếu bạn kết thúc với 150 chủ đề được lên lịch, bạn có thể tạo ra rất nhiều chi phí không cần thiết cho các bộ lập lịch được sử dụng trong Java và hệ điều hành để đồng thời xử lý chúng. Điều này chỉ đơn giản là không thể nếu bạn chỉ có 2 lõi CPU, trừ khi chủ đề của bạn đang chờ I/O hoặc như vậy tất cả các thời gian. Nhưng ngay cả trong trường hợp đó, nhiều chủ đề sẽ tạo ra rất nhiều I/O ...

Và vấn đề đó không xảy ra với FixedThreadPool, được tạo ra với ví dụ: 2 + n chủ đề, trong đó n là hợp lý thấp của khóa học, bởi vì với tài nguyên phần cứng và hệ điều hành được sử dụng với chi phí thấp hơn nhiều để quản lý các chủ đề mà không thể chạy anyway.

+0

Đôi khi không có lựa chọn nào tốt hơn, bạn chỉ có thể có 1 lõi CPU nhưng nếu bạn đang chạy máy chủ trong đó mọi yêu cầu của người dùng sẽ kích hoạt chuỗi để xử lý yêu cầu, sẽ không có bất kỳ lựa chọn hợp lý nào khác, đặc biệt nếu bạn có kế hoạch mở rộng quy mô máy chủ khi bạn phát triển cơ sở người dùng của mình. – mFeinstein

+0

@mFeinstein Làm thế nào người ta không có lựa chọn nếu một người ở vị trí chọn triển khai nhóm luồng?Trong ví dụ của bạn với 1 lõi CPU chỉ sinh ra nhiều luồng hơn, đơn giản là không có ý nghĩa gì, nó hoàn toàn phù hợp với ví dụ của tôi bằng cách sử dụng FixedThreadPool. Điều đó cũng dễ dàng cân nhắc, đầu tiên với trên hoặc hai luồng công nhân, sau đó với 10 hoặc 15 tùy thuộc vào số lượng lõi. –

+2

Phần lớn các triển khai máy chủ web sẽ tạo một chuỗi mới cho mỗi yêu cầu HTTP mới ... Chúng sẽ không quan tâm đến số lõi thực tế của máy, điều này giúp việc triển khai đơn giản và dễ dàng hơn. Điều này áp dụng cho nhiều thiết kế khác mà bạn chỉ muốn mã một lần và triển khai, và không phải biên dịch lại và triển khai lại nếu bạn thay đổi máy, đó có thể là một cá thể đám mây. – mFeinstein

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