一 什麼是線層阻塞?
線程阻塞即線程高風亮節讓出或放棄CPU,暫停執行,只有等到導致阻塞的原因解除,才能恢復運行;或者被其他線程中斷,該線程會退出阻塞狀態,並且拋出InterrutedException.
二 常見的導致線程阻塞的原因:
線程執行了Thread.sleep(intn)方法,線程放棄CPU,睡眠n毫秒,然後恢復運行。但是此時被該線程所控制的同步代碼塊的鎖定釋放權利依然屬於該線程,其他線程依然不能訪問該代碼塊。
線程要執行一段同步代碼,由於無法獲得相關的同步鎖,只好進入阻塞狀態,等到獲得了同步鎖才能恢復運行。
線程執行了一個對象的wait()方法,進入阻塞狀態,只有等到其他線程執行了該對象的notify()或notifyAll()方法,纔可將其喚醒。
線程執行I/O操作或進行遠程通信時,會因爲等待相關的資源而進入阻塞狀態。例如,當線程執行System.in.read()方法時,如果用戶沒有向控制檯輸入數據,則該線程會一直等到了用戶的輸入數據才才read()方法返回。
三 什麼是非阻塞
所謂非阻塞,就是指當線程執行某些方法時,如果操作還沒有就緒,就立即返回,而不會一直等到操作就緒。
四 什麼是阻塞I/O和非阻塞I/O
可能出現阻塞的輸入和輸出操作被稱爲阻塞I/O;於此對照,如果執行輸入和輸出操作時,不會發生阻塞,則稱爲非阻塞I/O。
五 Java.nio包提供了支持非阻塞通信的類
· ServerSocketChannel:ServerSocket的替代類,支持阻塞通信和非阻塞通信。
· SocketChannel:Socket的替代類,支持阻塞通信和非阻塞通信。
· Selector:爲ServerSocketChannel監控連接就緒事件,爲SocketChannel監控連接就緒,讀就緒和寫就緒事件。
· SelectionKey:ServerSocketChannel及SocketChannel註冊事件的句柄。當一個SelectionKey對象位於Selector對象的selector-keys集合中時,就表示與這個SeletionKey對象相關的事件發生了。
· Charset:字符編碼,提供了把字節流轉換爲字符串和把字符串轉換爲字節流的使用方法。
· Buffer:數據輸入和輸出往往是比較耗時的操作,緩衝區從兩個方面提高I/O操作的效率。
- 減少實際的物理讀寫次數;
- 緩衝區在創建時被分配內存,這塊內存區域一直被重用,這可以減少動態分配和回收內存區域的次數,可以通過修改緩衝區的極限屬性來達到緩衝區重用的目的;
- 緩衝區提供了三個屬性來控制緩衝區的使用:容量,極限和位置,詳見API;於此同時,提供了用於改變以上3個屬性的方法
Ø clear(): 把極限設置爲容量的值,再把位置設爲0
Ø flip(): 把極限設置爲位置的值,再把位置設爲0
Ø rewind():不改變極限的值,把位置設爲0
其中,flip()方法爲從Buffer中取數據做好了準備,而clear()則向Buffer中裝入數據做好準備。
Channel用來連接緩衝區與數據源或數據匯(數據目的地),通道創建時被打開,一旦關閉通道,就不能重新打開了。高級通道操作提供了分散讀取和集中寫數據的類和相關方法,可進一步的提高輸入和輸出操作的速度,詳見API文檔。
§具有自動增長的緩衝區的ChannelIO類
ChannelIO對SocketChannel進行了包裝,增加了自動增長緩衝區容量的功能。當調用socketChannel.read(ByteBuffer buffer)方法時,如果buffer已滿(position=limit)那麼即使通道中還有未接收的數據,read方法也不會讀取任何數據,而是直接返回0,表示讀到了0個字節。
爲了能讀取通道中的所有數據,必須保證緩衝區的容量足夠大。在ChannelIO類中,有一個requestBuffer變量,它用來存放客戶的HTTP請求數據,當requestBuffer剩餘容量已經不足5%,並且還有HTTP請求數據未接收時,ChannelIO會自動擴充requestBuffer的容量,該功能由resizeRequestBuffer()方法完成。
如下所示是ChannelIO類的源程序,它的read()和write()方法利用SocketChannel來接收和發送數據,並且它還提供了實用方法transferTo(),該方法能把文件中的數據發送到SockChannel中。
//此處省略import語句
Public class ChannelIO {
Protected SocketChannelsocketChannel;
//存放請求數據
Protected ByteBuffer requestBuffer;
Private static int requestBufferSize= 4096;
Public ChannelIO() throwsIOException {
this.socketChannel = socketChannel;
//設置模式爲阻塞模式或非阻塞模式
socketChannel.configureBlocking(blocking);
requestBuffer = ByteBuffer.allocate(requestBufferSize);
}
Public SocketChannel getSocketChannel(){
ReturnsocketChannel;
}
// 如果原緩衝區剩餘容量不夠,就創建新的緩衝區,容量爲原來的兩倍,把原緩衝區的數據複製到新緩衝區
Protected void resizeRequestBuffer(intremaining){
if (requestBuffer.remaining()< remianing){
//把容量增大到原來的兩倍
ByteBufferbb = ByteBuffer.allocate(requestBuffer.capcity()*2);
//把極限設置爲位置的值
requestBuffer.flip();
//把原來緩衝區中的數據複製到新的緩衝區
bb.put(requestBuffer);
requestBuffer= bb;
}
}
//接受數據,把它們存放到requestBuffer中,如果requestBuffer的剩餘容量不足5%,就通過resizeRequestBuffer(int remaining)方法擴充容量
Public int read() throws IOException {
resizeRequestBuffer(requestBufferSize/20);
returnsocketChannel.read(requestBuffer);
}
//返回requestBuffer,它存放了請求數據
Public ByteBuffer getReadBuf(){
ReturnrequestBuffer;
}
//發送參數指定的ByteBuffer中的數據
Public int write(ByteBuffer src) throwsIOException {
ReturnsocketChannel.write(src);
}
//把FileChannel中的數據寫到SocketChannel中
Public long transgerTo(FileChannel fc, longpos, long len) throws IOExcetion{
Returnfc.transferTo(pos,len,socketChannel);
}
//關閉SocketChannel
Public void close() throws IOException{
socketChannel.close();
}
}