1. 程式人生 > >JAVA NIO(四)阻塞式IO與非阻塞式IO

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 NIOI/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原則定