我有運行特定問題的算法組合的代碼,然後只要一個算法找到問題的答案,程序就會繼續。投資組合中的其他算法獲得自願信號終止,並且執行的主要線程繼續。可運行的最終變量的空指針異常
此代碼的一個用戶正在向我發送一個帶有NullPointerException的棧跟蹤 「resultReference.set(solverResult);」 從以下代碼可以看出,resultReference是一個最終變量,並且會立即初始化。我不知道它有可能變成空白。我花了很長時間試圖在我的結尾重現問題無濟於事。用戶堆棧跟蹤上的行號與我的代碼上的行號相匹配。用戶報告在3種不同的場合看到了錯誤,但很少(每次問題解決時都不會發生這種情況),所以也許會出現某種競爭狀態。這是jdk 1.8_25。
我是否正確地認爲這個錯誤應該是不可能的,因爲變量是最終的?我不確定該堆棧跟蹤應該怎麼做,並且希望有一些讓人放心的是它應該是不可能的。
public class ParallelSolver {
private final ListeningExecutorService executorService;
private final AtomicReference<Throwable> error;
private final List<Solver> solvers;
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ParallelSolver.class);
public ParallelSolver(int threadPoolSize, List<Solvers> solvers) {
executorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(threadPoolSize));
error = new AtomicReference<>();
this.solvers = solvers;
}
public SolverResult solve(Problem p) {
final AtomicReference<SolverResult> resultReference = new AtomicReference<>();
final List<Future> futures = new ArrayList<>();
final Semaphore workDone = new Semaphore(0);
try {
// Submit one job per each solver in the portfolio
solvers.forEach(solver -> {
final ListenableFuture<Void> future = executorService.submit(() -> {
SolverResult solverResult = solver.solve(p);
if (solverResult.isConclusive()) {
log.debug("Signalling the blocked thread to wake up!");
// NPE HERE ON THIS LINE
resultReference.set(solverResult);
workDone.release(solvers.size());
}
log.debug("Releasing a single permit as the work for this thread is done.");
workDone.release(1);
log.debug("Job ending...");
return null;
});
futures.add(future);
Futures.addCallback(future, new FutureCallback<Void>() {
@Override
public void onSuccess(Void result) {
}
@Override
public void onFailure(Throwable t) {
if (t instanceof CancellationException) {
return;
}
error.compareAndSet(null, t);
// Wake up the main thread (if it's still sleeping)
workDone.release(solvers.size());
}
});
});
// Wait for a thread to complete solving and signal you, or all threads to timeout
log.debug("Main thread going to sleep");
workDone.acquire(solvers.size());
log.debug("Main thread waking up, checking for errors then cancelling futures");
checkForErrors();
// cancel any still to be launched futures
futures.forEach(future -> future.cancel(false));
log.debug("Returning now");
return resultReference.get() == null ? SolverResult.createTimeoutResult() : resultReference.get();
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while running parallel job", e);
}
}
/**
* We want a fail-fast policy, but java executors aren't going to throw the exception on the main thread.
* We can't call Future.get() and check for errors, because that might block.
* So we set a variable when an error occurs, and check it here.
*/
private void checkForErrors() {
if (error.get() != null) {
log.error("Error occured while executing a task", error.get());
throw new RuntimeException("Error occurred while executing a task", error.get());
}
}
寫的代碼,'solvers'永遠不會被初始化,所以它總是'null'。我不知道'workDone'是什麼。 – FDinoff
謝謝,糾正 - 複製和粘貼代碼中的問題。 workDone是一個信號量,用於確保主線程阻塞,直到每個人都超時或者一個算法找到答案。 – newmanne
你可以發佈完整的堆棧跟蹤嗎?一個問題是NPE是源於標記行還是源自AtomicReference代碼的某處。 –