? ? ? ? 在閱讀BufferedInputStream的時候利耍,有些地方有點疑惑蚌本,在研究好久之后,似乎清晰了一點隘梨,在此記錄程癌。
首先,我們來看下這里的主要的成員:
DEFAULT_BUFFER_SIZE = 8912? ?很簡單轴猎,一個默認的buffer大小
MAX_BUFFER_SIZE =?Integer.MAX_VALUE-8; 這個是buffer最大的申請空間嵌莉,這里的-8大概是因為一些虛擬機會在數(shù)組頭部添加一些內容吧。
buf[];? 真正的buffer捻脖,由關鍵字volatile修飾锐峭,表示線程可見。
bufUpdater;一個進行原子操作更新buffer的對象
count? ?這是buffer在流中當前已經讀到的流大小
pos? ?位置可婶,一個在buffer中的位置來標示當前所讀到的位置沿癞。
markpos? ?為了進行重讀流,進行的一個mark標記位置矛渴,在之后執(zhí)行reset()的時候椎扬,pos會回到當前的markpos位置,以用來重復讀取數(shù)據。
mark?的常規(guī)協(xié)定是:如果方法?markSupported?返回?true蚕涤,則輸入流總會在調用?mark?之后記住所有讀取的字節(jié)晶府,并且無論何時調用方法?reset?,都會準備再次提供那些相同的字節(jié)钻趋。但是,如果在調用?reset?之前可以從流中讀取多于?readlimit?的字節(jié)剂习,則根本不需要該流記住任何數(shù)據蛮位。
marklimit? ? 標示在當前的markpos失效之前允許讀到的最大位置。在這里碰到問題鳞绕,為什么會有這個限制呢失仁,通過查詢相關資料我的理解就是當這個值太大的時候,或者pos過大的時候们何,buffer要保存被標記的字節(jié)的話就有些浪費資源萄焦,畢竟buffer是有限的。
接下來看看方法:
getBufIfOpen(): 這個方法用來獲取當前的buffer冤竹,如果buffer關閉拂封,則拋出異常。
構造器:用來創(chuàng)建一個制定大小的buffer字節(jié)數(shù)組鹦蠕。
在inputStream中主要的使用方法就是read(), 這里的這個方法
就是檢查當前已讀位置pos和已經讀入的流的大小冒签,來進行讀取數(shù)據,或者是繼續(xù)將流讀入buffer來獲取數(shù)據钟病。因為如果當前的buffer不足達到要讀取的地方萧恕,就會擴展buffer。
在這個方法中肠阱,fill()方法是最主要的一段邏輯票唆。
下面解讀一下fill()的目的。
1.當markpos < 0 屹徘,即未被標記走趋,則可以從buffer開頭進行讀入流,之后將流中的內容讀進buffer噪伊,如果buffer空間不足則會進行擴展操作吆视,稍后再提。將count賦值為讀入的字節(jié)大小酥宴。
2.如果當前位置已經達到了buffer的大小(這里我感覺只有=是成立的吧啦吧,有更好的見解請分享給我??),那么進行擴展或者清除動作拙寡。
? 2.1 markpos>0,標示存在mark授滓,就將markpos之后的數(shù)據刷新到buffer的開始。確保mark之后的數(shù)據在以后執(zhí)行reset()的時候可以重讀到。注意般堆,這里只是markpos>0,如果markpos=0,那么就不需要進行被mark的buffer的移位操作在孝。
? 2.2 當markpos=0并且當buffer大小超過了marklimit,(這里可以看到marklimit指的是從開始到pos=marklimit這個位置之間的數(shù)據吧)那么就進行清除標記操作淮摔,以便騰出buffer空間私沮。
? 2.3? buffer大小超過了最大限制,是不允許的操作和橙。
? 2.4? fill()主要是用來填充buffer仔燕,這里就是填充的操作。首先計算一下當前pos有沒有超過buffer最大大小MAX_BUFFER_SIZE的一半魔招,沒有超過就把新的大小nsz設置為pos的二倍晰搀;否則新的大小就是MAX_BUFFER_SIZE。然后比較nsz有沒有超過marklimit办斑,超過了就重新設置成marklimit的大型馑 (在這里發(fā)現(xiàn),這樣做的目的是要控制buffer大小乡翅,不要超過marklimit鳞疲,否則之后進行reset()的時候,會發(fā)現(xiàn)mark被清除導致異常發(fā)生蠕蚜。)然后進行一次buffer的擴展建丧,將當前位置pos之前的數(shù)據拷貝到增大容量后的buffer。之后的updater.compareAndSet()是做什么用的呢波势?如果說是拷貝數(shù)據那么前一句話的拷貝是做什么用的翎朱?
通過在此分析發(fā)現(xiàn),前面的拷貝操作只是將數(shù)據賦值給新的nbuf尺铣,并沒有刷新對象本身的buffer拴曲,這個就是利用原子操作來進行這個動作確保原數(shù)組引用指向新數(shù)據。
看一下read1()和read()
從read方法的循環(huán)中凛忿,可以看到nread = read1(b, off + n, len - n),再去read1中觀察澈灼,
計算還有多少可讀avail,如果不夠店溢,那么先檢查叁熔,如果markpos存在,同時要讀的大小超過了buffer大小床牧,那么就進行read()荣回。這兩個部分實現(xiàn)了縮小讀取范圍,直到len小于buffer的大小戈咳,那么就可以進行buffer的填充了心软,填充過后重新計算avail壕吹,重新計算讀取的范圍將buffer數(shù)據讀入拷貝到接受流b中,如果inputStream中沒有可用數(shù)據進行讀取删铃,那么就返回耳贬。
read()是貪婪讀取,即從buffer中不斷獲取數(shù)據猎唁,buffer不斷填充咒劲,直到inputStream沒有可讀數(shù)據。read1()是非貪婪的诫隅,只會讀到buffer的結束腐魂,這兩個方法組合使用來達到從inputStream中獲取足夠多的數(shù)據。
skip()
如果可讀數(shù)據不足阎肝,沒有mark的情況下直接從流inputStream中跳過;如果標記了mark肮街,那么先填充风题,重新計算avail,進行pos的跳躍操作嫉父。
close()
利用原子操作把buffer進行指向null沛硅,inputStream指向null并且關閉對應的流來實現(xiàn)對整個資源的釋放。
以上就是一些主要方法的理解绕辖,還有幾個問題摇肌,希望大家?guī)兔μ崾疽恍〇|西。
注:這里的mark是為了實現(xiàn)對流的重讀仪际,假如沒有標記围小,那么可以隨意操作。反之树碱,我們需要保證流的重讀性肯适,一些數(shù)據的重復獲取還是很重要的,除非被重讀標記的范圍marklimit過大占用太多的資源成榜,得不償失框舔,所以進行了標記復位操作來釋放一些資源。
BufferedInputStream主要是用于進行針對磁盤io或是其他設備來優(yōu)化的赎婚,用來提高效率刘绣,并且減少讀取次數(shù)可以減少設備的損耗。