将PipedWriter + PipedReader对的编写器线程中的错误委托给读者线程

时间:2014-07-30 09:40:43

标签: java multithreading io pipe

我想要什么?

我有一个带有两个线程的PipedWriter + PipedReader对。我想,在写入线程中捕获的异常将被包含在IOException中的PipedReader的下一个操作抛出。如果PipedReader本身有错误,则可以将写入线程异常添加到抑制的异常中。

为什么?

问题是如果在写入线程中发生错误,我只能关闭编写器,这会导致读取器端的EOF正常。我希望读者线程知道作者方面的错误。但我希望我的代码的用户应该看到一个简单的Reader。所以我必须将该错误表示为IOException。

问题

你知道任何开箱即用的#34;错误授权pipedwriter + reader"在那里实施?你会如何以线程安全的方式实现这样的事情?

1 个答案:

答案 0 :(得分:1)

解决方案

我创建了一个“ErrorDelegatingReaderDecorator”:

import java.io.IOException;
import java.io.Reader;

class ErrorDelegatingReaderDecorator extends Reader {
    static final String ERROR_MESSAGE_WRITER_THREAD = "Error occoured on the other side of the pipe. See the cause!";
    static final String ERROR_MESSAGE_READER_THREAD = "Error occoured on the this side of the pipe. See the cause!";
    private Reader decorated;
    private Throwable delegatedThrowable;

    public ErrorDelegatingReaderDecorator(Reader decorated) {
        super();
        this.decorated = decorated;
    }

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        Throwable originalException = null;
        int result = -1;
        try {
             result = decorated.read(cbuf, off, len);           
        }
        catch( Throwable original ) {
            originalException = original;
        }
        finally {
            throwException(originalException);
        }
        return result;
    }

    @Override
    public void close() throws IOException {
        Throwable originalException = null;
        try {
            decorated.close();          
        }
        catch( Throwable original ) {
            originalException = original;
        }
        finally {
            throwException(originalException);
        }
    }

    private synchronized void throwException(Throwable originalException) throws IOException {
        Throwable delegatedTemp = delegatedThrowable;
        delegatedThrowable = null;
        if ( originalException != null ) {
            if ( delegatedTemp != null ) {
                originalException.addSuppressed(delegatedTemp);
            }
            throw new IOException( ERROR_MESSAGE_READER_THREAD, originalException ) ;
        }
        else if ( delegatedTemp != null ) {
            throw new IOException( ERROR_MESSAGE_WRITER_THREAD, delegatedTemp );
        }
    }


    public synchronized void setDelegatedThrowable(Throwable delegatedThrowable) {
        this.delegatedThrowable = delegatedThrowable;
    }
}

然后我可以像这样使用它:

    final PipedWriter pipedWriter = new PipedWriter();
    PipedReader pipedReader = new PipedReader( pipedWriter, pipeBufferSize);
    final ErrorDelegatingReaderDecorator errorDelegatingReader = new ErrorDelegatingReaderDecorator( pipedReader );

    executorService.execute( new Runnable( ) {
        @Override
        public void run() {
            try 
            {
                //do something that writes into the writer and can throw an error
            } catch (Exception e) {
                errorDelegatingReader.setDelegatedThrowable(e);
                LOGGER.error( "Error while processing excel file.", e );
            }
            finally {
                try {
                    pipedWriter.close();
                } catch (IOException e) {
                    LOGGER.error( "Error while closing printwriter.", e );
                }
            }
        }
    });
    //Giving out the decorated reader to the reader thread.

权衡

因为我必须从装饰读者的方法的finally部分抛出异常,所以我必须抓住Throwable,我必须将其包装到IOException中。它有点难看,但它不太可能发生在装饰PipedReader上。

为什么它不能先工作

同样有趣的是我也告诉我,我是如何在发送EOF而不是抛出错误时出现间歇性失败:我在编写器块中使用了try-with-resources。这导致我的作家先被关闭,然后我在装饰器中设置了错误。如果两者之间我有一个阅读,那么它已成为一个EOF。所以我用普通的旧版块替换了它,现在订单没问题了。