![](/img/trans.png)
[英]In java how can I catch an exception thrown from child thread and then throw it again from the parent thread
[英]How can I make an unchecked exception thrown from a thread to be propagated to main thread?
我找不到一種方法(既不能通過SO也不能調試我的代碼)來允許將從胎面引發的異常傳播到主線程。 我已經嘗試使用Thread.setUncaughtExceptionHandler()
和CompletableFuture
: .exceptionally()
.handle()
,...
關鍵是,使用這些機制(在我調試的時候),實際的處理是在工作線程上執行的,而不是在主線程上執行的-我無法設法將其到達主線程。
總的來說,我正在編寫一個測試,並且如果在工作線程中引發異常,則它永遠不會到達運行測試的主線程,即使出現問題也可以使測試通過。
我需要該異常會引發異步; 我不能等待將來完成,因為我需要立即從主界面立即返回一個Stream(PipedStream)而不發生阻塞。
我得到的唯一提示是控制台錯誤(並且僅當我使用傳統的Thread
+ uncaughtExceptionHandler
方法時,當我嘗試使用CompletableFuture
時根本沒有日志):
Exception: com.example.exception.MyException thrown from the UncaughtExceptionHandler in thread "Thread-5"
如果我沒有定義異常處理程序,則為:
Exception in thread "Thread-5" com.example.MyException: Exception message
我提供一些代碼:
try {
@SuppressWarnings("squid:S2095") final PipedOutputStream pos = new PipedOutputStream();
final PipedInputStream pis = new PipedInputStream(pos);
CompletableFuture.runAsync(pipeDecryptorRunnable(inputStream, pos));
return pis;
} catch (IOException e) {
throw new CryptographyException(e.getMessage(), e);
}
在pipeDecryptorRunnable
內部是一個解密數據的CipherStream。 那里拋出異常。 但是我無法在主線程中將其捕獲並變得不可見。 從此方法返回的pis
流用於讀取,並且工作線程在讀取prom pis
實時解密數據。
編輯: UncaughtExceptionHandler
作為類似問題的答案表明不適用於我的情況,因為處理程序代碼是由工作線程而不是主線程調用的。
感謝@RalfKleberhoff的技巧,我提供了我所尋找的解決方案。 事實是,為了實現所需的行為,需要線程間通信機制。 考慮到我已經在使用PipedStream
,我可以利用它來實現目標–我還認為,在某種解決方案中,涉及事件總線來從一個線程向另一個線程(主線程/工作線程)發出信號或進行通信,我認為某些事件總線庫也可以實現它。
回到管道流中,我需要在主線程中讀取pis
並在工作線程中寫入其連接的pos
。 因此,當在worker中引發異常時,我需要主線程來注意到這一點。
為此,您可以擴展PipedOutputStream
類,添加一個方法以在發生異常時向連接的管道發出信號。 同樣,您需要擴展連接的PipedInputStream
以在發生異常時發出信號,存儲異常並覆蓋read方法,以檢查是否首先發生異常,在這種情況下,將異常包裝在讀取的IOException
中方法。
這里的代碼:
/**
* This piped stream class allows to signal Exception between threads, allowing an exception produced in the writing
* thread to reach the reading thread.
*
* @author Gerard on 10/11/2017.
*/
public class ExceptionAwarePipedOutputStream extends PipedOutputStream {
private final ExceptionAwarePipedInputStream sink;
public ExceptionAwarePipedOutputStream(ExceptionAwarePipedInputStream sink) throws IOException {
super(sink);
this.sink = sink;
}
/**
* Signals connected {@link ExceptionAwarePipedInputStream} that an exception ocurred allowing to propagate it
* across respective threads. This works as inter thread communication mechanism. So it allows to the reading thread
* notice that an exception was thrown in the writing thread.
*
* @param exception The exception to propagate.
*/
public void signalException(Throwable exception) {
sink.signalException(exception);
}
}
·
/**
* This piped stream class allows to signal Exception between threads, allowing an exception produced in the writing
* thread to reach the reading thread.
*
* @author Gerard on 10/11/2017.
*/
public class ExceptionAwarePipedInputStream extends PipedInputStream {
private volatile Throwable exception;
void signalException(Throwable exception) {
this.exception = exception;
}
@Override
public int read(byte[] b) throws IOException {
final int read = super.read(b);
checkException();
return read;
}
@Override
public synchronized int read() throws IOException {
final int read = super.read();
checkException();
return read;
}
@Override
public synchronized int read(byte[] b, int off, int len) throws IOException {
final int read = super.read(b, off, len);
checkException();
return read;
}
private void checkException() throws IOException {
if (exception != null) {
throw new IOException(exception.getMessage(), exception);
}
}
}
客戶端代碼:
public InputStream decrypt(InputStream inputStream) {
assert supportedStreamModes.contains(mode) : "Unsupported cipher mode for stream decryption " + mode;
@SuppressWarnings("squid:S2095") final ExceptionAwarePipedInputStream pis = new ExceptionAwarePipedInputStream();
final ExceptionAwarePipedOutputStream pos = newConnectedPipedOutputStream(pis);
final Cipher decryptor = newDecryptorInitialized(inputStream);
CompletableFuture.runAsync(
pipeDecryptorRunnable(inputStream, pos, decryptor));
return pis;
}
private ExceptionAwarePipedOutputStream newConnectedPipedOutputStream(ExceptionAwarePipedInputStream pis) {
try {
return new ExceptionAwarePipedOutputStream(pis);
} catch (IOException e) {
throw new CryptographyException(e.getMessage(), e);
}
}
和異常處理部分( 注意線程信令):
private Runnable pipeDecryptorRunnable(InputStream inputStream, ExceptionAwarePipedOutputStream pos, Cipher decryptor) {
return () -> {
try {
// do stuff... and write to pos
} catch (Exception e) {
// Signaling any (checked or unchecked) exception
pos.signalException(new CryptographyException(e.getMessage(), e));
} finally {
closePipedStream(pos);
}
};
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.