Tôi đang gặp phải một vấn đề trong đó nếu tôi cố gắng thay đổi kích thước nhóm ThreadPoolExecutor
lõi của một số khác sau khi nhóm được tạo, sau đó không liên tục, một số tác vụ bị từ chối RejectedExecutionException
mặc dù tôi không bao giờ gửi nhiều hơn queueSize + maxPoolSize
số lượng tác vụ.
Vấn đề mà tôi đang cố gắng giải quyết là mở rộng ThreadPoolExecutor
thay đổi kích thước các luồng cốt lõi của nó dựa trên các lệnh thực thi đang chờ xử lý trong hàng đợi của nhóm luồng. Tôi cần điều này bởi vì theo mặc định, a ThreadPoolExecutor
sẽ tạo một cái mới Thread
chỉ khi hàng đợi đầy.
Đây là một chương trình Pure Java 8 độc lập nhỏ thể hiện vấn đề.
import static java.lang.Math.max;
import static java.lang.Math.min;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolResizeTest {
public static void main(String[] args) throws Exception {
// increase the number of iterations if unable to reproduce
// for me 100 iterations have been enough
int numberOfExecutions = 100;
for (int i = 1; i <= numberOfExecutions; i++) {
executeOnce();
}
}
private static void executeOnce() throws Exception {
int minThreads = 1;
int maxThreads = 5;
int queueCapacity = 10;
ThreadPoolExecutor pool = new ThreadPoolExecutor(
minThreads, maxThreads,
0, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(queueCapacity),
new ThreadPoolExecutor.AbortPolicy()
);
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(() -> resizeThreadPool(pool, minThreads, maxThreads),
0, 10, TimeUnit.MILLISECONDS);
CompletableFuture<Void> taskBlocker = new CompletableFuture<>();
try {
int totalTasksToSubmit = queueCapacity + maxThreads;
for (int i = 1; i <= totalTasksToSubmit; i++) {
// following line sometimes throws a RejectedExecutionException
pool.submit(() -> {
// block the thread and prevent it from completing the task
taskBlocker.join();
});
// Thread.sleep(10); //enabling even a small sleep makes the problem go away
}
} finally {
taskBlocker.complete(null);
scheduler.shutdown();
pool.shutdown();
}
}
/**
* Resize the thread pool if the number of pending tasks are non-zero.
*/
private static void resizeThreadPool(ThreadPoolExecutor pool, int minThreads, int maxThreads) {
int pendingExecutions = pool.getQueue().size();
int approximateRunningExecutions = pool.getActiveCount();
/*
* New core thread count should be the sum of pending and currently executing tasks
* with an upper bound of maxThreads and a lower bound of minThreads.
*/
int newThreadCount = min(maxThreads, max(minThreads, pendingExecutions + approximateRunningExecutions));
pool.setCorePoolSize(newThreadCount);
pool.prestartAllCoreThreads();
}
}
Tại sao pool nên ném RejectionExecutException nếu tôi không bao giờ gửi thêm rằng queueCapacity + maxThreads. Tôi không bao giờ thay đổi các chủ đề tối đa vì vậy theo định nghĩa của ThreadPoolExecutor, nó sẽ phù hợp với nhiệm vụ trong một Chủ đề hoặc cho hàng đợi.
Tất nhiên, nếu tôi không bao giờ thay đổi kích thước nhóm, thì nhóm luồng không bao giờ từ chối bất kỳ nội dung gửi nào. Điều này cũng khó gỡ lỗi vì việc thêm bất kỳ sự chậm trễ nào trong bài nộp khiến vấn đề không còn nữa.
Bất kỳ con trỏ nào về cách sửa lỗi RejectionExecutException?
ThreadPoolExecutor
rất có thể là một ý tưởng tồi và bạn cũng không cần phải thay đổi mã hiện có trong trường hợp này chứ? Tốt nhất bạn nên cung cấp một số ví dụ về cách mã thực tế của bạn truy cập vào người thi hành. Tôi sẽ ngạc nhiên nếu nó sử dụng nhiều phương thức cụ thể ThreadPoolExecutor
(không phải trong ExecutorService
).
ExecutorService
bằng cách gói một cái hiện có, trong đó gửi lại các tác vụ không gửi được do thay đổi kích thước?