[英]Using Callable and ExecutorCompletionService, future.cancel() does not work
我正在使用一堆可調用對象來搜索單個塊中的列表,一旦返回 true,我想取消所有其他正在運行的可調用對象。 future.cancel 不會取消它們
我的號碼查找器
public class NumberFinderImpl implements NumberFinder {
// how many threads the exucutor will use to start the callables
private final int NUMBER_THREADS = 20;
// the amount of elements in the array we will search in each callable chunk
private final int CHUNK_ARRAY_SIZE = 5;
@Override
public boolean contains(int valueToFind, List<CustomNumberEntity> arrayToSearch) {
long startTime = System.nanoTime();
ExecutorService WORKER_THREAD_POOL = Executors.newFixedThreadPool(NUMBER_THREADS);
CompletionService<Boolean> completionService =
new ExecutorCompletionService<>(WORKER_THREAD_POOL);
int numberOfChunksNeeded = (int) Math.ceil(arrayToSearch.size() / CHUNK_ARRAY_SIZE);
// get a callable for each chunk search
List<Callable<Boolean>> callablesForEachChunkSearch =
getCallablesForEachChunk(
CHUNK_ARRAY_SIZE, numberOfChunksNeeded, valueToFind, arrayToSearch);
// start the callables and collect the futures
List<Future<Boolean>> futuresForCallables =
callablesForEachChunkSearch
.stream()
.map(completionService::submit)
.collect(Collectors.toList());
for (int j = 0; j < futuresForCallables.size(); j++) {
try {
// take().get() is blocking
// so if a callable is not done yet
// it will wait until it is before proceeding
Boolean chunkResult = completionService.take().get();
if (chunkResult) {
long endTime = System.nanoTime();
long timeTaken = endTime - startTime;
// TimeUnit
long timeInSeconds = TimeUnit.SECONDS.convert(timeTaken, TimeUnit.NANOSECONDS);
System.out.println("Search time in seconds" + timeInSeconds);
for (Future<Boolean> future : futuresForCallables) {
// cancel all the other running callables
future.cancel(true);
}
return true;
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
for (Future<Boolean> future : futuresForCallables) {
// cancel all the other running callables
future.cancel(true);
}
long endTime = System.nanoTime();
long timeTaken = endTime - startTime;
// TimeUnit
long timeInSeconds = TimeUnit.SECONDS.convert(timeTaken, TimeUnit.NANOSECONDS);
System.out.println("Search time in seconds" + timeInSeconds);
return false;
}
// get a list of callables that each search a certain chunk of the array
private List<Callable<Boolean>> getCallablesForEachChunk(
int chunkArraySize,
int numberOfChunksNeeded,
int valueToFind,
List<CustomNumberEntity> arrayToSearch) {
List<Callable<Boolean>> callableList = new ArrayList<>(numberOfChunksNeeded);
for (int i = 0; i < numberOfChunksNeeded; i++) {
int startPosForCallable = 0;
if (i > 0) {
startPosForCallable = i * chunkArraySize;
}
// dont let end pos go out of bounds
// if the chunk extends past the size, just set endPos as the end of the array
int endPosForCallable =
Math.min(startPosForCallable + chunkArraySize - 1, arrayToSearch.size());
Callable<Boolean> callableSearch =
new NumberFinderCallable(
arrayToSearch, valueToFind, startPosForCallable, endPosForCallable);
callableList.add(callableSearch);
}
return callableList;
}
我的可調用對象進行搜索
public class NumberFinderCallable implements Callable<Boolean> {
private List<CustomNumberEntity> arrayToSearch;
private int startPos;
private int endPos;
private int valueToSearchFor;
public NumberFinderCallable(
List<CustomNumberEntity> arrayToSearch, int valueToSearchFor, int startPos, int endPos) {
this.arrayToSearch = arrayToSearch;
this.startPos = startPos;
this.endPos = endPos;
this.valueToSearchFor = valueToSearchFor;
}
@Override
public Boolean call() {
System.out.println(
"Callable started, searching the chunk of array with start pos "
+ startPos
+ " and end pos "
+ endPos);
for (int i = startPos; i <= endPos; i++) {
System.out.println(
"Callable is comparing a number in pos "
+ i
+ " in the chunk with star pos "
+ +startPos
+ " and end pos "
+ endPos);
if (FastestComparator.compare(valueToSearchFor, arrayToSearch.get(i)) == 0) {
System.out.println("element found in pos " + i + ". Returning true");
return true;
}
}
return false;
}
}
即使在找到真實結果並且所有期貨都被取消后,我也可以從日志中看到線程仍在運行
根據您之前的問題,您已經明確表示,由於性能原因,您正在嘗試並行化此contains
方法,而不是為了了解 API。 但是,IMO 您錯誤地認為該方法實際上需要優化。
我對包含100000000
(1 億)個元素的列表進行了臨時測試,並針對最壞的情況,即嘗試查找列表中不存在的元素。 對於順序contain
方法
list.contains(Integer.MAX_VALUE)
平均大約需要:
0.25 seconds
以及使用流的並行版本:
list.parallelStream().anyMatch(i -> i.equals(Integer.MAX_VALUE))
平均大約需要:
0.19 seconds
在 4 核機器中加速1.32x
。 我非常懷疑一個人會取得比這更多的成就。 更不用說以下的可維護性和可讀性:
list.parallelStream().anyMatch(i -> i.equals(Integer.MAX_VALUE))
與使用顯式執行器等的潛在並行解決方案相比。
如果contain
方法在性能方面非常重要,並且列表中沒有重復的元素,則應該考慮使用HashSet
來獲得恆定復雜度時間的contain
方法。
ExecutorService WORKER_THREAD_POOL = Executors.newFixedThreadPool(NUMBER_THREADS);
ThreadPoolExecutor WORKER_THREAD_POOL = (ThreadPoolExecutor) Executors.newFixedThreadPool(NUMBER_THREADS);
然后執行purge()以釋放已取消期貨的線程,例如
future.cancel(true);
WORKER_THREAD_POOL.purge();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.