关于多线程中两个缓存包装流同时包装一个节点流的问题

作者: insightcodeyk
出处: https://insightcodeyk.github.io/2018/04/01/StreamInPackagingAndNode/
声明: 本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0,转载请注明作者及出处。

这个问题起源于对多线程文件上传的测试。在这个测试中,Server和Client通过TCP连接,Client请求向Server上传文件,Server接受请求后,把Client上传的文件存入指定位置。这原本是个非常简单的问题,但是在这个过程中出现了一个bug,在某些时候会出现文件上传不完整的问题。在多次排查后,发现问题的源头:
这里用了两个包装流一个是BufferedWriter用于获取文件名称,一个是BufferedInputStream用于获取文件内容,这两个包装流同时从socket获得OutputStream:

//这是获取文件名称的流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(sos));
bw.write(file.getName());
bw.newLine();

//这是获取文件内容的流
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(sos);

//两个流先后共用了从Client的Socket获得的OutputStream

在排查过程中发现在某些时候Server从bis中无法读到任何字节,从而丢失了部分上传内容。关于两个包装流先后使用一个节点流的情况JDK没有进行描述,google和baidu都没有类似的问题提出,因此只能看BufferedInputStream和BufferedWriter的源码,最后这个问题并没有在源码中获得很好的解释,因为read0()是一个native方法,无法得知JDK读取流中一个字节的具体处理过程。只要保证不要在开发过程中对同一个节点流使用不同包装流即可以避免这个问题。