1. 程式人生 > >java NIO(三)之阻塞與非阻塞

java NIO(三)之阻塞與非阻塞

                                                      阻塞與非阻塞

阻塞   傳統的 IO 流都是阻塞式的。也就是說,當一個執行緒呼叫 read() 或 write()時,該執行緒被阻塞,直到有一些資料被讀取或寫入,該執行緒在此期間不能執行其他任務。因此,在完成網路通訊進行 IO 操作時,由於執行緒會阻塞,所以伺服器端必須為每個客戶端都提供一個獨立的執行緒進行處理,當伺服器端需要處理大量客戶端時,效能急劇下降。

非阻塞  Java NIO 是非阻塞模式的。當執行緒從某通道進行讀寫資料時,若沒有資料可用時,該執行緒可以進行其他任務。執行緒通常將非阻塞 IO 的空閒時間用於在其他通道上執行 IO 操作,所以單獨的執行緒可以管理多個輸入和輸出通道。因此,NIO 可以讓伺服器端使用一個或有限幾個執行緒來同時處理連線到伺服器端的所有客戶端。

 1. 通道(Channel):負責連線

   java.nio.channels.Channel 介面:
|--SelectableChannel
|--SocketChannel
|--ServerSocketChannel
|--DatagramChannel


|--Pipe.SinkChannel
|--Pipe.SourceChannel


2. 緩衝區(Buffer):負責資料的存取


3. 選擇器(Selector):是 SelectableChannel 的多路複用器。用於監控 SelectableChannel 的 IO 狀況

阻塞式

	@Test
	public void client() throws Exception{
		//建立通道
		FileChannel open = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ);
		//分配緩衝區
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		//建立socket通道
		SocketChannel open2 = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9988));
		//讀取本地檔案,併發送到服務端
		while(open.read(buffer)!=-1){
			buffer.flip();
		open2.write(buffer);
		buffer.clear();
		}
		open2.shutdownOutput();
		//獲取服務端反饋
		int len=0;
		while((len=open2.read(buffer))!=-1){
			buffer.flip();
			System.out.println(new String (buffer.array(),0,len));
			buffer.clear();
		}
		open.close();
		open2.close();
	};
	
	@Test
	public void server() throws IOException{
		// 獲取通道
		FileChannel open = FileChannel.open(Paths.get("11.jpg"), StandardOpenOption.WRITE,
				StandardOpenOption.CREATE);
		ServerSocketChannel open2 = ServerSocketChannel.open();
		//分配指定大小的緩衝區
		ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
		//繫結連線
		open2.bind(new InetSocketAddress(9988));
		//獲取客戶端連線的通道
		SocketChannel accept = open2.accept();
		//接收客戶端的資料,並儲存到本地
		while(accept.read(byteBuffer)!=-1){
			byteBuffer.flip();
			open.write(byteBuffer);
			byteBuffer.clear();
		}
		

		//傳送反饋給客戶端
		byteBuffer.put("圖片收到了".getBytes());
		byteBuffer.flip();
		accept.write(byteBuffer);
		accept.close();
		open.close();
		open2.close();
	}


非阻塞式

	@Test
	public void client() throws Exception{
		//1. 獲取通道
		SocketChannel clientSoc=SocketChannel.open(new InetSocketAddress("127.0.0.1", 9988));
		//2. 切換非阻塞模式
		clientSoc.configureBlocking(false);
		//3. 分配指定大小的緩衝區
		ByteBuffer allocate = ByteBuffer.allocate(1024);
		//4. 傳送資料給服務端
		Scanner sc=new Scanner(System.in);
		while(sc.hasNext()){
			String next = sc.next();
			allocate.put((LocalDate.now()+"--客戶端說"+"\n"+next).getBytes());
			allocate.flip();
			clientSoc.write(allocate);
			allocate.clear();
		}
		sc.close();
		//5. 關閉通道
		clientSoc.close();
	}
	
	
	@Test
	public void Server() throws Exception{
		//1. 獲取通道
		ServerSocketChannel serSoc=ServerSocketChannel.open();
		//2. 切換非阻塞模式
		serSoc.configureBlocking(false);
		//3. 繫結連線
		serSoc.bind(new InetSocketAddress(9988));
		//4. 獲取選擇器
		Selector selector = Selector.open();
		//5. 將通道註冊到選擇器上, 並且指定“監聽接收事件”
		serSoc.register(selector,  SelectionKey.OP_ACCEPT);
		//6. 輪詢式的獲取選擇器上已經“準備就緒”的事件
		while(selector.select()>0){
			//7. 獲取當前選擇器中所有註冊的“選擇鍵(已就緒的監聽事件)”
			Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
			while(iterator.hasNext()){
				//8. 獲取準備“就緒”的是事件
				SelectionKey sk = iterator.next();
				//9. 判斷具體是什麼事件準備就緒
				if(sk.isAcceptable()){
					//10. 若“接收就緒”,獲取客戶端連線
					SocketChannel sChannel = serSoc.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
			iterator.remove();
		}
		
	}

註冊Selector  

serSoc.register(selector,  SelectionKey.OP_ACCEPT);

當呼叫 register(Selector sel, int ops) 將通道註冊選擇器時,選擇器

對通道的監聽事件,需要通過第二個引數 ops 指定。
可以監聽的事件型別(用 可使用 SelectionKey  的四個常量 表示):
    讀 : SelectionKey.OP_READ (1)
    寫 : SelectionKey.OP_WRITE (4)
    連線 : SelectionKey.OP_CONNECT (8)
    接收 : SelectionKey.OP_ACCEPT (16)

  若註冊時不止監聽一個事件,則可以使用“位或”操作符連線。

int interestKey=SelectionKey.OP_ACCEPT|SelectionKey.OP_WRITE;

SelectionKey:表示 SelectableChannel 和 Selector 之間的註冊關係。每次向
選擇器註冊通道時就會選擇一個事件(選擇鍵)。選擇鍵包含兩個表示為整
數值的操作集。操作集的每一位都表示該鍵的通道所支援的一類可選擇操作。

方 法 描 述

int interestOps()                              獲取感興趣事件集合
int readyOps()                                    獲取通道已經準備就緒的操作的集合
SelectableChannel channel()             獲取註冊通道
Selector selector()                              返回選擇器
boolean isReadable()                         檢測 Channal 中讀事件是否就緒
boolean isWritable()                           檢測 Channal 中寫事件是否就緒
boolean isConnectable()                    檢測 Channel 中連線是否就緒
boolean isAcceptable()                       檢測 Channel 中接收是否就緒

選擇 器(Selector )的應用

Selector  的常用方法

方 法 描 述
Set<SelectionKey> keys()     所有的 SelectionKey 集合。代表註冊在該Selector上的Channel
selectedKeys()                        被選擇的 SelectionKey 集合。返回此Selector的已選擇鍵集
int select()                           監控所有註冊的Channel,當它們中間有需要處理的 IO 操作時,該方法返回,並將對應得的 SelectionKey 加入被選擇的SelectionKey 集合中,該方法返回這些 Channel 的數量。
int select(long timeout)         可以設定超時時長的 select() 操作
int selectNow()                      執行一個立即返回的 select() 操作,該方法不會阻塞執行緒
Selector wakeup()                  使一個還未返回的 select() 方法立即返回
void close()                            關閉該選擇器

UDP的傳送

@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();
		}
	}

單向管道

	@Test
	public void test01() throws IOException{
		//1. 獲取管道
		Pipe pipe = Pipe.open();
		
		//2. 將緩衝區中的資料寫入管道
		ByteBuffer buf = ByteBuffer.allocate(1024);
		
		Pipe.SinkChannel sinkChannel = pipe.sink();
		buf.put("通過單向管道傳送資料".getBytes());
		buf.flip();
		sinkChannel.write(buf);
		
		//3. 讀取緩衝區中的資料
		Pipe.SourceChannel sourceChannel = pipe.source();
		buf.flip();
		int len = sourceChannel.read(buf);
		System.out.println(new String(buf.array(), 0, len));
		
		sourceChannel.close();
		sinkChannel.close();
	}

相關推薦

java NIO()阻塞阻塞

                                                      阻塞與非阻塞阻塞   傳統的 IO 流都是阻塞式的。也就是說,當一個執行緒呼叫 read() 或 write()時,該執行緒被阻塞,直到有一些資料被讀取或寫入,該執行緒

Java NIO()阻塞阻塞

   阻塞與非阻塞 阻塞   傳統的 IO 流都是阻塞式的。也就是說,當一個執行緒呼叫 read() 或 write()時,該執行緒被阻塞,直到有一些資料被讀取或寫入,該執行緒在此期間不能執行其他任務。因此,在完成網路通訊進行 IO 操

java nio及作業系統底層原理同步非同步阻塞阻塞

目錄 IO基本概念 同步,非同步,阻塞,非阻塞 同步與非同步 阻塞與非阻塞 IO模型(Reference Link) 阻塞I/O模型 非阻塞I/O模型 I/O複用模型 訊號驅動非同步I/O模型 非同步I/O模型 總結 AIO,BIO,NIO Jav

Java 同步異步-阻塞阻塞理解

blog markdown logs 任務 一段 mar 慢操作 兩個 需要 Java 同步與異步-阻塞與非阻塞理解 Java 中同步與異步,阻塞與非阻塞都是用來形容交互方式,區別在於它們描述的是交互的兩個不同層面。 同步與異步 同步與異步更關註交互雙方是否可以同時工作。以

NIO阻塞阻塞

NIO的非阻塞模式 NIO完成核心的東西是一個選擇器,Selector,選擇器主要是將每一個傳輸資料的通道註冊到選擇器上,選擇器作用是監控這些IO的狀態(讀,寫,連線狀態),然後用選擇器監控通道的狀況,等待所有的執行緒準備就緒時,選擇器將任務分配到服務端一個或者多個執行緒上再去執行

Java NIO學習總結一(阻塞特性)

NIO(New IO)是從Java 1.4版開始引入的新的IO API,其與標準JAVA IO API的差異本質上體現在資源的利用方式上,這一點可以從現實中餐廳排隊的例子來理解。午飯時間到了,小明準備從三家備選餐廳A、B、C中選擇一家就餐,糟糕的是三家餐廳的位置都滿了,小明

Java NIO怎麼理解通道和阻塞

nio引入了buffer、channel、selector等概念。 通道相當於之前的I/O流。 “通道”太抽象了。java解釋不清的東西只能看它底層是怎麼解釋的——作業系統的I/O控制,通道控制方式? I/O裝置:CPU——通道——裝置控制器——I/O裝置 (通道和裝置控制

IONIO的區別,阻塞阻塞的區別

阻塞概念:應用程式在獲取網路資料的時候,如果網路傳輸資料很慢,那麼程式就一直等著,知道傳輸完畢為止。 非阻塞概念:應用程式直接可以獲取到已經轉備好的資料,無需等待。 IO為同步阻塞形式,NIO為同步非阻塞形式、NIO並沒有實現非同步,在JDK1.7之後,升級

iphone開發輕鬆搞定原生socket 程式設計,阻塞阻塞,收發自如

iphone socket 開發 在iphone的平臺下,要進行socket開發其實有很多種的方法,開源的庫Asyncsocket,官方的CFSocket,還有BSD的socket。 這裡要做一個簡單的socket普及,這裡包含在socket的設定非阻塞喝超時的控制邏輯,

linux裝置驅動阻塞阻塞I/O

先做一下與核心阻塞有關的知識儲備: 1)程序休眠:     程序休眠,簡單的說就是正在執行的程序讓出CPU。休眠的程序會被核心擱置在在一邊,只有當核心再次把休眠的程序喚醒,程序才會會重新在CPU執行。這是核心中的程序排程。一個CPU在同一時間只能有一個程序在執行,微觀序列巨

Socket編程中,阻塞阻塞的區別

軟件 復用 優點 調用 服務器 運用 需要 默認 con   阻塞:一般的I/O操作可以在新建的流中運用.在服務器回應前它等待客戶端發送一個空白的行.當會話結束時,服務器關閉流和客戶端socket.如果在隊列中沒有請示將會出現什麽情況呢?那個方法將會等待一個的到來.這個行為

同步異步、阻塞阻塞

阻塞與非阻塞 就會 結束 檢查 通信機制 得到 node 分布 好書 “阻塞”與"非阻塞"與"同步"與“異步"不能簡單的從字面理解,提供一個從分布式系統角度的回答。1.同步與異步同步和異步關註的是消息通信機制 (synchronous communication/ a

阻塞阻塞,同步異步

通過 部件 一個 socket 沒有 事件觸發 sel syn 就會 在進行網絡編程時,我們常常見到同步(Sync)/異步(Async),阻塞(Block)/非阻塞(Unblock)四種調用方式:同步: 所謂同步,就是在發出一個功能調用時,在沒有得到結果之前,

同步異步,阻塞阻塞

消息 阻塞 結果 阻塞與非阻塞 過程調用 函數 異步 非阻塞 完成 異步的概念和同步相對。當一個同步調用發出後,調用者要一直等待返回消息(結果)通知後,才能進行後續的執行;當一個異步過程調用發出後,調用者不能立刻得到返回消息(結果)。實際處理這個調用的部件在完成後,通過狀態

關於veriolg中阻塞阻塞賦值問題

觸發 改變 希望 到來 決定 工作 執行 為什麽 個人 在一開始學到阻塞和非阻塞的時候,所被告知的兩者的區別就在於阻塞是串行的,非阻塞是並行的。但是雖然知道這個不同點,有些時候還是很難真正區分用兩者電路的區別,在這就通過幾個例子來解釋一下。 以一個簡單的串行流水線寄存器為例

轉:聊聊同步、異步、阻塞阻塞

AI strong 什麽 商業 同步與異步 好的 等待 不難 兩個 轉載:https://www.jianshu.com/p/aed6067eeac9 近來遇到了一些常見的概念,尤其是網絡編程方面的概念,如:阻塞、非阻塞、異步I/O等等,對於這些概念自己也沒有太清晰的認

關於flock文件鎖的阻塞阻塞

open bsp 等待 fopen pan ech 直接 else 阻塞與非阻塞 阻塞模式,程序會一直等待。 <?php $fp = fopen("lock.txt", "r"); if(flock($fp,LOCK_EX)) { //code flock($fp,

socket阻塞阻塞 同步非同步 I/O模型

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

socket阻塞阻塞,同步非同步、I/O模型(轉載只為查閱方便,若有侵權,立刪)

socket阻塞與非阻塞,同步與非同步 作者:huangguisu     1. 概念理解        在進行網路程式設計時,我們常常見到同步(Sync)/非同步(Async),阻塞(Block)/非阻塞(Unbl

redux-saga generator巢狀執行的阻塞阻塞

1.generator呼叫generator 在one中yield另一個generatoranother function*another(params){ // ... } function*one(params,{ call, put }){ // ...