IO(同步阻塞)
-
IO流分類:
字符流:Reader,Writer
字節(jié)流:InputStream,OutputStream

注意:對于字節(jié)流的輸入輸出是相對于java平臺來說的,所以當(dāng)要讀取文件時,相當(dāng)于將文件內(nèi)容輸入到j(luò)ava平臺,所以要用InputStream,寫入文件時,相當(dāng)于將文件內(nèi)容從java平臺輸出到文件中,所以要用OutputStream.
-
字符流操作的注意事項(xiàng):
寫入文件必須要用flush()刷新
用完一定要記得關(guān)閉流
創(chuàng)建一個文件時,如果該目錄下有同名文件,將被覆蓋
在讀取文件時,必須保證該文件已存在,否則拋出異常
字符流的緩沖區(qū)的出現(xiàn)是為了提高流的操作效率而出現(xiàn)的
需要被提高效率的流作為參數(shù)傳遞給緩沖區(qū)的構(gòu)造函數(shù)
在緩沖區(qū)中封裝了一個數(shù)組,存入數(shù)據(jù)后一次取出
-
字節(jié)流操作的注意事項(xiàng)
字節(jié)流操作可以不用刷新流操作
InputStream特有方法:int available()(返回文件中的字節(jié)個數(shù))
NIO( 多路復(fù)用 ,同步非阻塞)
同步與異步(synchronous/asynchronous):同步(等待返回)是一種可靠的有序運(yùn)行機(jī)制,當(dāng)我們進(jìn)行同步操作時,后續(xù)的任務(wù)是等待當(dāng)前調(diào)用返回,才會進(jìn)行下一步;而異步(不等待,回調(diào))則相反,其他任務(wù)不需要等待當(dāng)前調(diào)用返回,通常依靠事件、回調(diào)等機(jī)制來實(shí)現(xiàn)任務(wù)間次序關(guān)系
阻塞與非阻塞:在進(jìn)行阻塞操作時,當(dāng)前線程會處于阻塞狀態(tài),無法從事其他任務(wù),只有當(dāng)條件就緒才能繼續(xù),比如ServerSocket新連接建立完畢,或者數(shù)據(jù)讀取、寫入操作完成;而非阻塞則是不管IO操作是否結(jié)束,直接返回,相應(yīng)操作在后臺繼續(xù)處理
很多時候,人們也把 java.net下面提供的部分網(wǎng)絡(luò) API,比如 Socket、ServerSocket、HttpURLConnection 也歸類到同步阻塞 IO 類庫,因?yàn)榫W(wǎng)絡(luò)通信同樣是 IO 行為。
-
三個組成部分:
-
Channel :管道
通過它讀取和寫入數(shù)據(jù)??梢园阉醋鍪荌O中的流 ,但是Channel是雙向的,,既可以讀又可以寫,而流是單向的
Channel可以進(jìn)行異步的讀寫
對Channel的讀寫必須通過buffer對象
-
Channel 類型
FileChannel:從文件讀取數(shù)據(jù)的
DatagramChannel:讀寫UDP網(wǎng)絡(luò)協(xié)議數(shù)據(jù)
SocketChannel:讀寫TCP網(wǎng)絡(luò)協(xié)議數(shù)據(jù)
ServerSocketChannel:可以監(jiān)聽TCP連接
-
buffer:緩沖區(qū)
所有數(shù)據(jù)都通過Buffer對象處理,所以,永遠(yuǎn)不會將字節(jié)直接寫入到Channel中,相反,是將數(shù)據(jù)寫入到Buffer中;同樣,也不會從Channel中讀取字節(jié),而是將數(shù)據(jù)從Channel讀入Buffer,再從Buffer獲取這個字節(jié)
NIO讀寫數(shù)據(jù)的中轉(zhuǎn)池。Buffer實(shí)質(zhì)上是一個數(shù)組,通常是一個字節(jié)數(shù)據(jù),但也可以是其他類型的數(shù)組。但一個緩沖區(qū)不僅僅是一個數(shù)組,重要的是它提供了對數(shù)據(jù)的結(jié)構(gòu)化訪問,而且還可以跟蹤系統(tǒng)的讀寫進(jìn)程。
-
使用 Buffer 讀寫數(shù)據(jù) 的四個步驟
1.寫入數(shù)據(jù)到 Buffer, Buffer 會記錄下寫了多少數(shù)據(jù) ;
2.調(diào)用 flip() 方法, 將 Buffer 切換模式;
3.從 Buffer 中讀取數(shù)據(jù), 可以讀取之前寫入到 Buffer 的所有數(shù)據(jù)。 ;
4.調(diào)用 clear() 方法或者 compact() 方法, 清空緩沖區(qū),讓它可以再次被寫入。有兩種方式能清空緩沖區(qū):調(diào)用 clear() 或 compact() 方法。clear() 方法會清空整個緩沖區(qū)。compact() 方法只會清除已經(jīng)讀過的數(shù)據(jù)。任何未讀的數(shù)據(jù)都被移到緩沖區(qū)的起始處,新寫入的數(shù)據(jù)將放到緩沖區(qū)未讀數(shù)據(jù)的后面。
-
buffer分類:
ByteBuffer
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
-
Selector
Selector是一個對象,它可以注冊到很多個Channel上,監(jiān)聽各個Channel上發(fā)生的事件,并且能夠根據(jù)事件情況決定Channel讀寫。這樣,通過一個線程管理多個Channel,就可以處理大量網(wǎng)絡(luò)連接了。 在高并發(fā)中還可以減少線程的占用,和切換線程上下文的消耗
注冊一個channel到selector上
-
Selector selector = Selector.open();
channel.configureBlocking(false);
SelectionKey key =channel.register(selector,SelectionKey.OP_READ);
//op_READ :注冊對各種 I/O 事件興趣的地方,
注意: 注冊的Channel 必須設(shè)置成異步模式 才可以,否則異步IO就無法工作,這就意味著我們不能把一個FileChannel注冊到Selector,因?yàn)镕ileChannel沒有異步模式,但是網(wǎng)絡(luò)編程中的SocketChannel是可以的。 、
- 請注意對register()的調(diào)用的返回值是一個SelectionKey。 SelectionKey 代表這個通道在此 Selector 上注冊。當(dāng)某個 Selector 通知您某個傳入事件時,它是通過提供對應(yīng)于該事件的 SelectionKey 來進(jìn)行的。SelectionKey 還可以用于取消通道的注冊。
NIO多路復(fù)用
主要步驟和元素:
- 首先,通過 Selector.open() 創(chuàng)建一個 Selector,作為類似調(diào)度員的角色。
- 然后,創(chuàng)建一個 ServerSocketChannel,并且向 Selector 注冊,通過指定 SelectionKey.OP_ACCEPT,告訴調(diào)度員,它關(guān)注的是新的連接請求。
- Selector 阻塞在 select 操作,當(dāng)有 Channel 發(fā)生接入請求,就會被喚醒。
- 在 具體的 方法中,通過 SocketChannel 和 Buffer 進(jìn)行數(shù)據(jù)操作
IO 都是同步阻塞模式,所以需要多線程以實(shí)現(xiàn)多任務(wù)處理。而 NIO 則是利用了單線程輪詢事件的機(jī)制,通過高效地定位就緒的 Channel,來決定做什么,僅僅 select 階段是阻塞的,可以有效避免大量客戶端連接時,頻繁線程切換帶來的問題,應(yīng)用的擴(kuò)展能力有了非常大的提高
雖然NIO在網(wǎng)絡(luò)操作中,提供了非阻塞的方法,但是NIO的IO行為還是同步的。對于NIO來說,我們的業(yè)務(wù)線程是在IO操作準(zhǔn)備好時,得到通知,接著就由這個線程自行進(jìn)行IO操作,IO操作本身是同步的。
AIO
對AIO來說,則更加進(jìn)了一步,它不是在IO準(zhǔn)備好時再通知線程,而是在IO操作已經(jīng)完成后,再給線程發(fā)出通知。因此AIO是不會阻塞的,此時我們的業(yè)務(wù)邏輯將變成一個回調(diào)函數(shù),等待IO操作完成后,由系統(tǒng)自動觸發(fā)。