JAVA NIO(四)阻塞式IO與非阻塞式IO
1.阻塞與非阻塞
IO模型:
由於程序是不可直接訪問外部裝置的,所以只能呼叫核心去呼叫外部的裝置(上下文切換),然後外部裝置比如磁碟,讀出儲存在裝置自身的資料傳送給核心緩衝區,核心緩衝區在copy資料到使用者程序的緩衝區。包含兩個步驟:一將資料讀到核心,二將資料從核心copy使用者地址空間(即應用程式)。
① 阻塞與非阻塞:是針對於網路通訊而言。應用程式在獲取網路資料的時候,根據IO操作的就緒狀態來採取的不同方式。說白了是一種讀取或者寫入操作函式的實現方式。(即在服務端accept()時,若資料沒有準備就緒,則程式被阻塞)(執行一個應用程式,就啟動了一個使用者程序,作業系統的核心是核心程序)阻塞:應用程式在獲取網路資料的時候,如果網路傳輸資料很慢,那麼程式就一直等著,知道傳輸完畢為止。阻塞IO模型:
在等待資料時,程序被掛起(就像執行緒中的wait()),知道資料就緒。
非阻塞IO模型
非阻塞是指在核心請求IO裝置響應指令發出後,資料就開始準備,在此期間使用者程序沒有阻塞,也就是沒有掛起,它一直在詢問或者check資料有沒有傳送到kernel buffer(核心)中,忙等…。但是第二個階段(資料從kernel buffer複製到使用者程序空間)依然是阻塞的。但這種IO模型會大量的佔用CPU的時間,效率很低效,很少使用。
IO多路複用
在核心請求IO裝置響應指令發出後,資料就開始準備,在此期間使用者程序是阻塞的。資料從kernel buffer複製到使用者程序的過程也是阻塞的。但是和阻塞I/O所不同的是,它可以同時阻塞多個I/O操作,而且可以同時對多個讀操作,多個寫操作的I/O函式進行檢測,直到有資料可讀或可寫時,才真正呼叫I/O操作函式,也就是說一個執行緒可以響應多個請求。(在IO multiplexing Model中,實際中,對於每一個socket,一般都設定成為non-blocking,但是,如下圖所示,整個使用者的process其實是一直被block的。只不過process是被select這個函式block,而不是被socket IO給block
弊端:
select 如果任何一個sock(I/O stream)出現了資料,select 僅僅會返回,但是並不會告訴你是那個sock上有資料,於是你只能自己一個一個的找,10幾個sock可能還好,要是幾萬的sock每次都找一遍,這個無謂的開銷就頗有海天盛筵的豪氣了。
② 選擇器
選擇器(Selector) 是 SelectableChannel 物件的多路複用器,Selector 可以同時監控多個 SelectableChannel 的 IO 狀況,也就是說,利用 Selector 可使一個單獨的執行緒管理多個 Channel。Selector 是非阻塞 IO 的核心。
(舉慄:比如說資料就緒,當有資料就緒服務端才會進行accept();如圖所示)
當呼叫 register(Selector sel, int ops) 將通道註冊選擇器時,選擇器對通道的監聽事件,需要通過第二個引數 ops 指定。
可以監聽的事件型別(可使用 SelectionKey 的四個常量表示):
讀 : SelectionKey.OP_READ (1)
寫 : SelectionKey.OP_WRITE (4)
連線 : SelectionKey.OP_CONNECT (8)
接收 : SelectionKey.OP_ACCEPT (16)
若註冊時不止監聽一個事件,則可以使用“位或”操作符連線。
ssChannel.register(selector, SelectionKey.OP_ACCEPT | SelectionKey.OP_CONNECT);
常用方法③ SelectionKey表示 SelectableChannel 和 Selector 之間的註冊關係。每次向選擇器註冊通道時就會選擇一個事件(選擇鍵)。選擇鍵包含兩個表示為整數值的操作集。操作集的每一位都表示該鍵的通道所支援的一類可選擇操作。常用方法
2.阻塞IO
package com.dason.nio2;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import org.junit.Test;
/*
* 一、使用 NIO 完成網路通訊的三個核心:
*
* 1. 通道(Channel):負責連線
*
* java.nio.channels.Channel 介面:
* |--SelectableChannel
* |--SocketChannel
* |--ServerSocketChannel
* |--DatagramChannel
*
* |--Pipe.SinkChannel
* |--Pipe.SourceChannel
*
* 2. 緩衝區(Buffer):負責資料的存取
*
* 3. 選擇器(Selector):是 SelectableChannel 的多路複用器。用於監控 SelectableChannel 的 IO 狀況
*
*/
public class TestBlockingNIO {
//客戶端
@Test
public void client() throws IOException{
//1. 獲取通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
//2. 分配指定大小的緩衝區
ByteBuffer buf = ByteBuffer.allocate(1024);
//3. 讀取本地檔案,併發送到服務端
while(inChannel.read(buf) != -1){
buf.flip();
sChannel.write(buf);
buf.clear();
}
//4. 關閉通道
inChannel.close();
sChannel.close();
}
//服務端
@Test
public void server() throws IOException{
//1. 獲取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//2. 繫結連線
ssChannel.bind(new InetSocketAddress(9898));
//3. 獲取客戶端連線的通道
SocketChannel sChannel = ssChannel.accept();
//4. 分配指定大小的緩衝區
ByteBuffer buf = ByteBuffer.allocate(1024);
//5. 接收客戶端的資料,並儲存到本地
while(sChannel.read(buf) != -1){
buf.flip();
outChannel.write(buf);
buf.clear();
}
//6. 關閉通道
sChannel.close();
outChannel.close();
ssChannel.close();
}
}
3.非阻塞IO
package com.dason.nio2;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
import org.junit.Test;
/*
* 一、使用 NIO 完成網路通訊的三個核心:
*
* 1. 通道(Channel):負責連線
*
* java.nio.channels.Channel 介面:
* |--SelectableChannel
* |--SocketChannel
* |--ServerSocketChannel
* |--DatagramChannel
*
* |--Pipe.SinkChannel
* |--Pipe.SourceChannel
*
* 2. 緩衝區(Buffer):負責資料的存取
*
* 3. 選擇器(Selector):是 SelectableChannel 的多路複用器。用於監控 SelectableChannel 的 IO 狀況
*
*/
public class TestNonBlockingNIO {
//客戶端
@Test
public void client() throws IOException{
//1. 獲取通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
//2. 切換非阻塞模式
sChannel.configureBlocking(false);
//3. 分配指定大小的緩衝區
ByteBuffer buf = ByteBuffer.allocate(1024);
//4. 傳送資料給服務端
Scanner scan = new Scanner(System.in);
while(scan.hasNext()){
String str = scan.next();
buf.put((new Date().toString() + "\n" + str).getBytes());
buf.flip();
sChannel.write(buf);
buf.clear();
}
//5. 關閉通道
sChannel.close();
}
//服務端
@Test
public void server() throws IOException{
//1. 獲取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
//2. 切換非阻塞模式
ssChannel.configureBlocking(false);
//3. 繫結連線
ssChannel.bind(new InetSocketAddress(9898));
//4. 獲取選擇器
Selector selector = Selector.open();
//5. 將通道註冊到選擇器上, 並且指定“監聽接收事件”
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
//6. 輪詢式的獲取選擇器上已經“準備就緒”的事件
while(selector.select() > 0){
//7. 獲取當前選擇器中所有註冊的“選擇鍵(已就緒的監聽事件)”
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while(it.hasNext()){
//8. 獲取準備“就緒”的是事件
SelectionKey sk = it.next();
//9. 判斷具體是什麼事件準備就緒
if(sk.isAcceptable()){
//10. 若“接收就緒”,獲取客戶端連線
SocketChannel sChannel = ssChannel.accept();
//11. 切換非阻塞模式
sChannel.configureBlocking(false);
//12. 將該通道註冊到選擇器上
sChannel.register(selector, SelectionKey.OP_READ);
}else if(sk.isReadable()){
//13. 獲取當前選擇器上“讀就緒”狀態的通道
SocketChannel sChannel = (SocketChannel) sk.channel();
//14. 讀取資料
ByteBuffer buf = ByteBuffer.allocate(1024);
int len = 0;
while((len = sChannel.read(buf)) > 0 ){
buf.flip();
System.out.println(new String(buf.array(), 0, len));
buf.clear();
}
}
//15. 取消選擇鍵 SelectionKey
it.remove();
}
}
}
}
package com.dason.nio2;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
import org.junit.Test;
public class TestNonBlockingNIO2 {
@Test
public void send() throws IOException{
DatagramChannel dc = DatagramChannel.open();
dc.configureBlocking(false);
ByteBuffer buf = ByteBuffer.allocate(1024);
Scanner scan = new Scanner(System.in);
while(scan.hasNext()){
String str = scan.next();
buf.put((new Date().toString() + ":\n" + str).getBytes());
buf.flip();
dc.send(buf, new InetSocketAddress("127.0.0.1", 9898));
buf.clear();
}
dc.close();
}
@Test
public void receive() throws IOException{
DatagramChannel dc = DatagramChannel.open();
dc.configureBlocking(false);
dc.bind(new InetSocketAddress(9898));
Selector selector = Selector.open();
dc.register(selector, SelectionKey.OP_READ);
while(selector.select() > 0){
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while(it.hasNext()){
SelectionKey sk = it.next();
if(sk.isReadable()){
ByteBuffer buf = ByteBuffer.allocate(1024);
dc.receive(buf);
buf.flip();
System.out.println(new String(buf.array(), 0, buf.limit()));
buf.clear();
}
}
it.remove();
}
}
}
相關推薦
JAVA NIO(三)通道Channel & 直接與非直接緩衝區
1. 通道負責目標節點與源節點的連線;傳輸資料。在 Java NIO 中負責緩衝區中資料的傳輸。Channel 本身不儲存資料,因此需要配合緩衝區進行傳輸。2.通道的主要實現類 java.nio.channels.Channel 介面: |--FileChannel |
JAVA NIO(四)阻塞式IO與非阻塞式IO
1.阻塞與非阻塞IO模型:由於程序是不可直接訪問外部裝置的,所以只能呼叫核心去呼叫外部的裝置(上下文切換),然後外部裝置比如磁碟,讀出儲存在裝置自身的資料傳送給核心緩衝區,核心緩衝區在copy資料到使用者程序的緩衝區。包含兩個步驟:一將資料讀到核心,二將資料從核心copy使用
【原創】java-NIO(一)阻塞IO與非阻塞IO--轉載請註明出處
零、一個小故事 在講解阻塞IO與非阻塞IO之前,先舉出一個小小的例子: 一個老闆經營一個飯店,最初的時候,每來一個客人安排一個服務員招呼,客人很滿意。 後來客人越來越多,需要的服務員越來越多,但是餐廳的後廚已經擠滿了服務員,不
Java-NIO(七):阻塞IO與非阻塞IO
阻塞IO 傳統的 IO 流都是阻塞式的。 也就是說,當一個執行緒呼叫 read() 或 write()時,該執行緒被阻塞,直到有一些資料被讀取或寫入,該執行緒在此期間不能執行其他任務。 因此,在完成網路通訊進行 IO 操作時,由於執行緒會阻塞,所以伺服器端必
【原創】java-NIO(一)阻塞IO與非阻塞IO
零、一個小故事 在講解阻塞IO與非阻塞IO之前,先舉出一個小小的例子: 一個老闆經營一個飯店,最初的時候,每來一個客人安排一個服務員招呼,客人很滿意。 後來客人越來越多,需要的服務員越來越多,但是餐廳的後廚已經擠滿了服務員,不能請更多的服務員了,之前的
JAVA基礎24-多執行緒(四)【讀寫鎖,阻塞佇列,執行緒池】
一、讀寫鎖 使用步驟 二、阻塞佇列 (BlockingQueue) 提供執行緒安全的佇列訪問方式; 當阻塞佇列進行插入資料時,若佇列滿,則執行緒阻塞,直到佇列非滿的時候 當阻塞佇列取資料時,若佇列為空,則執行緒阻塞直到佇列非空時候。
java學習(四)代碼的設計
方法 聯系 string 封裝 代碼 his 站點 add 團隊 一、目的 1、為了使程序員編寫的代碼更加的簡潔,使人閱讀起來更加流暢 2、將運算代碼與界面代碼完全分離開來,利於團隊開發,提高團隊之間的工作效率 3、 在很短的時間內可以替換整個站點的外觀; 4、使程
java學習(四)static靜態變量 和this
java學習 方便 private setname 局部變量 變量 告訴 應該 size java中的this /* this:是當前類的對象引用。簡單的記,它就代表當前類的一個對象。 註意:誰調用這個方法,在該方法內部的this就代表誰
java並發編程(8)原子變量和非阻塞的同步機制
turn 判斷 變量 ntp 機制 tail values 添加 get 原子變量和非阻塞的同步機制 一、鎖的劣勢 1.在多線程下:鎖的掛起和恢復等過程存在著很大的開銷(及時現代的jvm會判斷何時使用掛起,何時自旋等待) 2.volatile:輕量級別的同步機制,
Java-Maven(四):Eclipse集成Maven環境配置
查找 epo 情況 jpg ont 使用 eclipse版 需要 style 一般maven都需要集成到IDE上使用的,而不是單獨的使用,常見的maven可集成IDE:eclipse、IntelliJ IDEA。但這裏就只學習eclipse集成maven的基礎上,進行mav
Java-NIO(八):DatagramChannel
reg lin mov div 數據 selector sca gist put Java NIO中的DatagramChannel是一個能收發UDP包的通道。操作步驟: 1)打開 DatagramChannel 2)接收/發送數據 同樣它也支持NIO的非阻塞模式操作
Java-NIO(九):管道 (Pipe)
png bsp java nio pri java-nio ges 線程 pre logs Java NIO 管道是2個線程之間的單向數據連接。Pipe有一個source通道和一個sink通道。數據會被寫到sink通道,從source通道讀取。 代碼使用示例:
Java學習(四)
div ++ 編譯 指針 c++ 所有 end xtend 利用 Java中類的操作與C++中基本相同,但舍棄了一些比較復雜但實際用處不多的操作,比如多繼承,在此用接口來表示。 1 package helloWorld; 2 3 public class Clas
Java基礎(四)
抽象類 使用 發生 註意 方法 類實例化 類方法 內容 大寫字母 一、方法 1、方法的定義 方法也叫函數,就是一個能獨立完成某個功能的一段代碼。方法可以看作一個整體。 語法: 修飾符 返回類型 方法名字(數據類型 變量名,數據類型 變量名,……[形式參數(0個到n
Java NIO(一)I/O模型概述
簡單的 什麽是 -c nbsp 流程 pos 文件 star 非阻塞 基本概念講述 什麽是同步? 同步就是:如果有多個任務或者事件要發生,這些任務或者事件必須逐個地進行,一個事件或者任務的執行會導致整個流程的暫時等待,這些事件沒有辦法並發地執行。 什麽是異步? 異步
JAVA基礎(四)面試題
Java基礎 Java程序員面試 面試題: 構造代碼塊,構造方法,靜態代碼的優先級? 靜態代碼塊>構造代碼塊>構造方法 面試題: overload和override的區別?overload:方法重載方法名一樣,參數不同,和返回值沒有關系參數不同:1)參數個數不同2)參數類型不同over
Java練習(四)--分解質因數
pre stat 輸入9 分析 新的 重復執行 質數 n) code 題目:將一個正整數分解質因數。例如:輸入90,打印出90=2*3*3*5。 程序分析:對n進行分解質因數,應先找到一個最小的質數k,然後按下述步驟完成: (1)如果這個質數恰等於n,則說明分解質因數的過程
Java的this、public、static、protected關鍵字——有C++基礎的Java入門(四)
目錄 一、 this關鍵字 1、 概念 2、 例項 3、 常見用法 4、 原理 二、 public關鍵字 三、static關鍵字 1、修飾在屬性上 2、修飾在方法上 四、 protected關鍵字 一、 this關鍵字 我覺得thi
Java併發(四):volatile的實現原理 Java併發(一):Java記憶體模型乾貨總結
synchronized是一個重量級的鎖,volatile通常被比喻成輕量級的synchronized volatile是一個變數修飾符,只能用來修飾變數。 volatile寫:當寫一個volatile變數時,JMM會把該執行緒對應的本地記憶體中的共享變數重新整理到主記憶體。 volatile讀:當讀一
Java併發(四):happens-before
happens-before 一個操作執行的結果需要對另一個操作可見,那麼這兩個操作之間必須存在happens-before關係 happen-before原則是JMM中非常重要的原則,它是判斷資料是否存在競爭、執行緒是否安全的主要依據,保證了多執行緒環境下的可見性。 happens-before原則定