標準 Java I/O 流在內部通信場景下存在性能短板:ByteArrayOutputStream 的 toByteArray() 需複製數據,PipedInputStream/OutputStream 同步開銷大且緩衝區固定。本文作為系列第二部分,由 Java 加密專家 Merlin Hughes 開發的一組無同步、零拷貝、可配置滯後閾值的高性能流替代方案,針對字節數組流與管道流進行深度優化。
多數流使用場景為單線程訪問,強制同步屬不必要開銷。參照 Collections 框架設計,預設不提供線程安全,需時由應用層包裝(如 Streams.synchronizedOutputStream())。本文實現的流類均移除 synchronized 關鍵字,僅在管道流這類天生多線程協作場景保留最精簡同步。
byte[] buffer,寫入時按需翻倍擴容(capacity * 2 + length)shared 標記:當 toInputStream() 返回只讀視圖時置真,後續 reset() 或寫入會觸發緩衝區複製(Copy-on-Write),確保只讀視圖數據不被污染writeTo(OutputStream) 直接輸出內部緩衝區;toInputStream() 返回 BytesInputStream 直接讀取共享數組,避免 toByteArray() 的全量複製public InputStream toInputStream() {
shared = true;
return new BytesInputStream(buffer, 0, index);
}
public void reset() throws IOException {
if (shared) { buffer = new byte[capacity]; shared = false; }
index = 0;
}
移除同步、修正 reset() 行為(恢復至 mark 位置而非初始偏移),支援 mark/reset、skip、available 等標準操作,構造函數允許指定讀取區間(offset, length),適配共享緩衝區場景。
capacity * hysteresis(預設 75%)或發生關閉/異常時才喚醒阻塞線程,批量處理減少喚醒次數Broken pipeSystem.arraycopy 一次處理連續區段,跨邊界最多兩次複製setTimeout() 防死鎖,setNonBlocking() 可拋出 InterruptedIOExceptionprivate int checkedAvailable(boolean rw) throws IOException {
while ((available = available(rw)) == 0) {
brokenCheck(rw); closedCheck(); exceptionCheck();
if (!nonBlocking) { sleeping = true; wait(timeout); }
else throw new InterruptedIOException(rw ? "Pipe full" : "Pipe empty");
}
return available;
}
private void processed(boolean rw, int amount) {
if (rw == READER) readx = (readx + amount) % (capacity * 2);
else writex = (writex + amount) % (capacity * 2);
if (sleeping && available(!rw) >= level) { notify(); sleeping = false; }
}
| 流類型 | 15KB | 15MB |
|---|---|---|
| 標準管道流 | 21 ms | 20,675 ms |
| 新管道流 | 0.68 ms | 158 ms |
| 標準字節數組流 | 0.31 ms | 745 ms |
| 新字節數組流 | 0.26 ms | 438 ms |
新管道流大數據場景下快 130 倍,字節數組流得益於零拷貝提升 40%。雖管道流仍慢於單線程工程方案,但已逼近理論極限。
// 字節數組流壓縮
public static InputStream newBruteForceCompress(InputStream in) throws IOException {
BytesOutputStream sink = new BytesOutputStream();
Streams.io(in, new GZIPOutputStream(sink)).close();
return sink.toInputStream();
}
// 管道流異步壓縮
public static InputStream newPipedCompress(final InputStream in) throws IOException {
PipeInputStream source = new PipeInputStream();
new Thread(() -> {
try (GZIPOutputStream gzip = new GZIPOutputStream(source.getOutputStream())) {
Streams.io(in, gzip);
} catch (IOException ex) { source.getOutputStream().setException(ex); }
}).start();
return source;
}
通過移除不必要同步、採用批量數組操作、引入滯後機制與零拷貝設計,新流類在完全兼容標準 InputStream/OutputStream API 前提下,將內部 I/O 性能提升 1-2 個數量級。同樣優化思路適用於其他高頻數據搬運場景。