<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # IO 程序與運行時數據在內存中駐留,由CPU負責執行,涉及到數據交換的地方,如磁盤、網絡等,就需要IO接口。IO包括輸入流(Input Stream)和輸出流(Output Stream),來表達數據從一端到另一端的過程。 Input Stream和Output Stream可以以內存為參照標準,加載到內存的是輸入流,從內存輸出到別的地方的是輸出流。比如File存于磁盤中,程序獲取File數據用來進行其它操作,這是將數據讀入內存中的過程,所以為輸入流。反之,程序將各種信息保存入File中,是將數據讀出內存的過程,所以為輸出流。 再比如,網絡操作,請求從客戶端來到服務端,也就是數據從客戶端到達了服務端,那么對于客戶端,是輸出流,對服務端,是輸入流,響應則相反。如圖: ![](https://img.kancloud.cn/09/3b/093b803870cb2bc92dd264564b6a2400_1080x962.png) ## IO原理 ![](https://img.kancloud.cn/3d/86/3d86784adf37082c3edca7f261ad4dcf_1080x805.png) 對于操作系統而言,JVM只是一個用戶進程,處于用戶態空間中,處于用戶態空間的進程是不能操作底層硬件的(如磁盤、網卡)。 用戶態的進程要訪問磁盤和網卡,必須通過系統調用,從用戶態切換到內核態才行。因此Java IO讀取數據時,用戶進程發起讀操作,會導致“syscall read”系統調用來從磁盤或網絡讀取數據;用戶進程發起寫操作,會導致“syscall write”系統調用來寫入到磁盤中或發送到網絡中。 1、由于局部性原理,操系統不會每次只讀取一個字節(代價太大),而是一次性讀取一片(一個或者若干個磁盤塊)的數據。 2、用戶態與內核態的轉化是一個耗時操作,因此IO操作時應盡量減少轉化操作。 基于以上兩點,需要有一個“中間緩沖區”——即內核緩沖區。用戶(JVM)發起讀操作時,系統先把數據從磁盤讀到內核緩沖區中,然后再把數據從內核緩沖區搬到用戶緩沖區;用戶(JVM)發起寫操作時,先把數據從用戶緩沖區搬到內核緩沖區,再把數據從內核緩沖區寫入到磁盤或網絡中。 # Java IO 數據在兩個設備之前的傳輸序列稱為流,設備可以為文件、網絡、內存,傳輸的內容可以為基本類型、序列化對象、本地化字符集 ## 分類 根據處理數據類型不同分為:字節流和字符流。 * 字節流:以字節為單位(1Byte)。字節流能處理所有類型的數據 * 字符流:以字符為單位,根據編碼格式一次可能讀取多個字節。字符流只能處理字符類型的數據 根據流的方向不同可分為:輸入流和輸出流 * 輸入流:表示從一個設備源讀取數據,只能進行讀操作 * 輸出流:表示向一個目標設備寫數據,只能進行寫操作 Java 相關類: 類|說明 ---|--- InputStream|字節輸入流 OutputStream|字節輸出流 Reader|字符輸入流 Writer|字符輸出流 Java 流類圖結構: ![](https://img.kancloud.cn/4a/fa/4afab522ed0e162403add91f7f9a2ea4_687x763.png) ## InputStream InputStream 類是所有輸入字節流的父類。 其常用子類包括:ByteArrayInputstream、StringBufferInputStream 和 FileInputStream。分別代表從 Byte 數組、StringBuffer 和 本地文件中讀取數據 ## OutputStream OutputStream 類是所有輸出字節流的父類。 其常用子類包括:ByteArrayOutputstream 和 FileOutputStream。分別代表向 Byte 數組和本地文件中寫入數據 ## Reader Reader 類是所有輸入字符流的父類。 其常用子類包括:CharReader 和 StringReader。分別代表從 Char 數組和 String 中讀取數據 ## Writer Writer 類是所有輸出字符流的父類。 其常用子類包括:CharArrayWriter 和 StringWriter。分別代表向 Char 數組和 String 中寫入數據 ## BufferedOutputStream與BufferedInputStream ### BufferedInputStream 構建一個輸入流,示例如下: ```java DataInputStream dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream("test.txt"))); ``` 使用FileInputStream的read方法讀取一個文件時,每次讀取一個字節,就需要訪問一次磁盤,頻繁操作磁盤,效率低下。為了減少訪問磁盤的次數,提高文件讀取的性能, Java提供了BufferedInputstream類,為其他輸入流提供緩沖功能。 創建BufferedInputStream時,會通過構造函數為其指定某個輸入流為參數,BufferedInputStream會將該輸入流分批讀取,每次讀取一部分到緩沖區中。當程序需要讀取數據時,直接從緩沖區進行讀取,由于是從內存緩沖區中讀取數 據,比每次從磁盤讀取效率高很多。 BufferedInputStream的緩沖區: ```java // 默認緩沖區大小:8Kb private static final int DEFAULT_BUFFER_SIZE = 8192; // 存儲數據的緩沖區 protected volatile byte buf[]; // 當前緩沖區的數據索引 protected int pos; // 構造方法 public BufferedInputStream(InputStream in) { this(in, DEFAULT_BUFFER_SIZE); } public BufferedInputStream(InputStream in, int size) { super(in); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; } ``` 下面來看看從緩沖區讀取數據的代碼: ```java public synchronized int read(byte b[], int off, int len) throws IOException { // 通過查看緩沖區buf是否為null,來判斷流是否被釋放 getBufIfOpen(); // 檢查b[]是否放得下數據 if ((off | len | (off + len) | (b.length - (off + len))) < 0) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int n = 0; for (;;) { // 調用私有方法read1來讀取數據,nread為已讀取到的字節數,沒有讀取或讀取不到數據返回-1 int nread = read1(b, off + n, len - n); // 讀不到數據了,返回 if (nread <= 0) return (n == 0) ? nread : n; n += nread; // 已經讀到了目標長度的數據,返回 if (n >= len) return n; // 如果輸入流已關閉,返回 InputStream input = in; if (input != null && input.available() <= 0) return n; } } private int read1(byte[] b, int off, int len) throws IOException { // 緩沖區的有效數據量 int avail = count - pos; if (avail <= 0) { // 需要讀取的數據量大于緩沖區的大小,此時使用緩沖區無意義 if (len >= getBufIfOpen().length && markpos < 0) { // 直接交給InputStream去讀取 return getInIfOpen().read(b, off, len); } // 緩沖區已經沒有數據可以讀取,進行填充數據 fill(); // 緩沖區有效數據量 avail = count - pos; // InputsStream中已經沒有有效數據可以讀取 if (avail <= 0) return -1; } int cnt = (avail < len) ? avail : len; // 將緩沖區的數據讀入b[] System.arraycopy(getBufIfOpen(), pos, b, off, cnt); // 更新緩沖區索引位置 pos += cnt; return cnt; } ``` 其中填充緩沖區方法fill的代碼如下: ```java private void fill() throws IOException { // 獲取緩沖區 byte[] buffer = getBufIfOpen(); if (markpos < 0) { pos = 0; } else if (pos >= buffer.length) { if (markpos > 0) { int sz = pos - markpos; System.arraycopy(buffer, markpos, buffer, 0, sz); pos = sz; markpos = 0; } else if (buffer.length >= marklimit) { markpos = -1; pos = 0; } else if (buffer.length >= MAX_BUFFER_SIZE) { throw new OutOfMemoryError("Required array size too large"); } else { /* grow buffer */ int nsz = (pos <= MAX_BUFFER_SIZE - pos) ? pos * 2 : MAX_BUFFER_SIZE; if (nsz > marklimit) nsz = marklimit; byte nbuf[] = new byte[nsz]; System.arraycopy(buffer, 0, nbuf, 0, pos); if (!bufUpdater.compareAndSet(this, buffer, nbuf)) { throw new IOException("Stream closed"); } buffer = nbuf; } } count = pos; // 從InputStream讀取數據到緩沖區 int n = getInIfOpen().read(buffer, pos, buffer.length - pos); if (n > 0) { count = n + pos; } } ``` ### BufferedOutputStream 構建一個輸出流,示例如下: ```java DataOutputStream dataOutputStream = new?DataOutputStream(new?BufferedOutputStream(new?FileOutputStream("filePath"))); ``` BufferedOutputStream與BufferedInputStream類似,只不過方向是相反的。創建BufferedOutputStream時,也會在構造函數中為其指定一個輸出流作為參數,BufferedOutputStream會從OutputStream中分批接收數據并放入緩沖區中,當緩沖區已滿時,調用flush方法觸發系統調用,將緩沖區數據寫入文件或網絡。 來看看分批將數據寫入緩沖區的write方法: ```java public synchronized void write(byte b[], int off, int len) throws IOException { // 要寫出的數據大于緩沖區的容量,就不使用緩沖區策略了 if (len >= buf.length) { // 先將緩沖區數據寫出 flushBuffer(); // 調用OutputStream的write方法直接將數據寫出 out.write(b, off, len); return; } // 要寫出的數據大于緩沖區的剩余容量 if (len > buf.length - count) { // 先將緩沖區的數據寫出 flushBuffer(); } // 將要寫出的數據寫入到緩沖區 System.arraycopy(b, off, buf, count, len); // 更新緩沖區已添加的數據容量 count += len; } ``` 看看flushBuffer方法: ```java private void flushBuffer() throws IOException { if (count > 0) { out.write(buf, 0, count); count = 0; } } ``` 代碼很簡單,就是將緩沖區中的數據寫入到輸出流中。 ## Java IO總結 IO緩沖區的存在,減少了系統調用的次數,提高了效率,但有緩沖區存在必然存在copy的過程,當涉及到雙流操作時,比如從一個輸入流讀入,寫入到一個輸出流中,就會存在冗余copy的操作,如下: * 從輸入流讀出到緩沖區 * 從緩沖區copy到b[] * 將b[] copy到輸出流緩沖區 * 輸出緩沖區的數據再讀出數據到輸出流 上面的情況存在冗余copy操作,我們來看看Okio是怎么處理的。 # Okio Okio使用Segment來做數據存儲,代碼如下: ```kotlin // 存儲具體數據的數組 @JvmField val data: ByteArray // 有效數據索引起始位置 @JvmField var pos: Int = 0 // 有效數據索引結束位置 @JvmField var limit: Int = 0 // 指示Segment是否為共享狀態 @JvmField var shared: Boolean = false // 指示當前Segment是否為數據擁有者 @JvmField var owner: Boolean = false // 指向下一個Segment @JvmField var next: Segment? = null // 指向上一個Segment @JvmField var prev: Segment? = null companion object { // 默認容量大小 const val SIZE = 8192 // 最小分享數據量 const val SHARE_MINIMUM = 1024 } ``` Segment被設計成可以分割的,用pos和limit來標記有效的數據范圍,用owner和shared來標識Segment是owner還是被共享者;同時,Segment可以采用雙向鏈表結構進行連接。 # 參考 [Java IO深入理解BufferedInputStream]([https://blog.csdn.net/yhl\_jxy/article/details/79318713](https://blog.csdn.net/yhl_jxy/article/details/79318713)) [Okio好在哪里?](https://url.cn/5xHNb05)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看