首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

徹底轉變流(二):高性能Java內部I/O流實現指南

     編輯:Java 相關知識

標準 Java I/O 流在內部通信場景下存在性能短板:ByteArrayOutputStream 的 toByteArray() 需複製數據,PipedInputStream/OutputStream 同步開銷大且緩衝區固定。本文作為系列第二部分,由 Java 加密專家 Merlin Hughes 開發的一組無同步、零拷貝、可配置滯後閾值的高性能流替代方案,針對字節數組流與管道流進行深度優化。

同步機制的取捨

多數流使用場景為單線程訪問,強制同步屬不必要開銷。參照 Collections 框架設計,預設不提供線程安全,需時由應用層包裝(如 Streams.synchronizedOutputStream())。本文實現的流類均移除 synchronized 關鍵字,僅在管道流這類天生多線程協作場景保留最精簡同步。

BytesOutputStream:零拷貝字節數組輸出流

核心設計

  • 內部維護動態擴容的 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;
}

BytesInputStream:輕量級字節數組輸入流

移除同步、修正 reset() 行為(恢復至 mark 位置而非初始偏移),支援 mark/reset、skip、available 等標準操作,構造函數允許指定讀取區間(offset, length),適配共享緩衝區場景。

PipeInputStream:高吞吐管道流

性能痛點分析

  1. 標準管道緩衝區僅 1KB,大數據傳輸頻繁阻塞喚醒
  2. 數組操作退化為單字節循環,且每操作加鎖,鎖競爭劇烈
  3. 單字節可用即喚醒線程,導致上下文切換頻繁,有效負載極低

優化策略

  • 環狀緩衝區:可配置大小(預設 8KB),讀寫索引取模回繞
  • 滯後閾值:只有當可用數據/空間達到 capacity * hysteresis(預設 75%)或發生關閉/異常時才喚醒阻塞線程,批量處理減少喚醒次數
  • 單讀單寫約定:限制僅一對線程訪問,簡化狀態檢查,若對端線程死亡自動拋出 Broken pipe
  • 基於數組的批量複製:System.arraycopy 一次處理連續區段,跨邊界最多兩次複製
  • 超時與非阻塞模式:setTimeout() 防死鎖,setNonBlocking() 可拋出 InterruptedIOException

關鍵邏輯:檢查可用性與索引更新

private 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; }
}

性能對比(800MHz Linux, JDK 1.4)

流類型15KB15MB
標準管道流21 ms20,675 ms
新管道流0.68 ms158 ms
標準字節數組流0.31 ms745 ms
新字節數組流0.26 ms438 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 個數量級。同樣優化思路適用於其他高頻數據搬運場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved