JavaIO 流核心模塊與基本原理

?一惊搏、IO 流與系統(tǒng)

IO 技術(shù)在 JDK 中算是極其復(fù)雜的模塊梭姓,其復(fù)雜的一個(gè)關(guān)鍵原因就是 IO 操作和系統(tǒng)內(nèi)核的關(guān)聯(lián)性,另外網(wǎng)絡(luò)編程屠升,文件管理都依賴(lài) IO 技術(shù)潮改,而且都是編程的難點(diǎn),想要整體理解 IO 流汇在,先從 Linux 操作系統(tǒng)開(kāi)始殖告。

Linux 空間隔離

Linux 使用是區(qū)分用戶(hù)的宝与,這個(gè)是基礎(chǔ)常識(shí)诽里,其底層也區(qū)分用戶(hù)和內(nèi)核兩個(gè)模塊:

User space:用戶(hù)空間

Kernel space:內(nèi)核空間

常識(shí)用戶(hù)空間的權(quán)限相對(duì)內(nèi)核空間操作權(quán)限弱很多墓懂,這就涉及到用戶(hù)與內(nèi)核兩個(gè)模塊間的交互榜跌,此時(shí)部署在服務(wù)上的應(yīng)用如果需要請(qǐng)求系統(tǒng)資源础浮,則在交互上更為復(fù)雜:

用戶(hù)空間本身無(wú)法直接向系統(tǒng)發(fā)布調(diào)度指令,java學(xué)習(xí)交流:737251827必須通過(guò)內(nèi)核靴姿,對(duì)于內(nèi)核中數(shù)據(jù)的操作维雇,也是需要先拷貝到用戶(hù)空間铝侵,這種隔離機(jī)制可以有效的保護(hù)系統(tǒng)的安全性和穩(wěn)定性猾警。

參數(shù)查看

可以通過(guò) Top 命令動(dòng)態(tài)查看各項(xiàng)數(shù)據(jù)分析温自,進(jìn)程占用資源的狀況:

us:用戶(hù)空間占用 CPU 的百分比;

sy:內(nèi)核空間占用 CPU 的百分比鸠踪;

id:空閑進(jìn)程占用 CPU 的百分比械媒;

wa:IO 等待占用 CPU 的百分比;

對(duì)wa指標(biāo)评汰,在大規(guī)模文件任務(wù)流程里是監(jiān)控的核心項(xiàng)之一纷捞。

IO 協(xié)作流程

此時(shí)再看上面圖【1】的流程,當(dāng)應(yīng)用端發(fā)起 IO 操作的請(qǐng)求時(shí)被去,請(qǐng)求沿著鏈路上的各個(gè)節(jié)點(diǎn)流轉(zhuǎn)兰绣,有兩個(gè)核心概念:

節(jié)點(diǎn)交互模式:同步與異步;

IO 數(shù)據(jù)操作:阻塞與非阻塞编振;

這里就是文件流中常說(shuō)的:【同步/異步】IO,【阻塞/非阻塞】IO,下面看細(xì)節(jié)。

二踪央、IO 模型分析

1臀玄、同步阻塞

用戶(hù)線(xiàn)程與內(nèi)核的交互方式,應(yīng)用端請(qǐng)求對(duì)應(yīng)一個(gè)線(xiàn)程處理畅蹂,整個(gè)過(guò)程中 accept(接收)和 read(讀取)方法都會(huì)阻塞直至整個(gè)動(dòng)作完成:

在常規(guī) CS 架構(gòu)模式中健无,這是一次 IO 操作的基本過(guò)程,該方式如果在高并發(fā)的場(chǎng)景下液斜,客戶(hù)端的請(qǐng)求響應(yīng)會(huì)存在嚴(yán)重的性能問(wèn)題累贤,并且占用過(guò)多資源。

2少漆、同步非阻塞

在同步阻塞 IO 的基礎(chǔ)上進(jìn)行優(yōu)化臼膏,當(dāng)前線(xiàn)程不會(huì)一直等待數(shù)據(jù)就緒直到完成復(fù)制:

在線(xiàn)程請(qǐng)求后會(huì)立即返回,并不斷輪詢(xún)直至拿到數(shù)據(jù)示损,才會(huì)停止輪詢(xún)渗磅,這種模式的缺陷也是顯而易見(jiàn)的,如果數(shù)據(jù)準(zhǔn)備好检访,在通知線(xiàn)程完成后續(xù)動(dòng)作始鱼,這樣就可以省掉很多中間交互。

3脆贵、異步通知模式

在異步模式下医清,徹底摒棄阻塞機(jī)制,過(guò)程分段進(jìn)行交互卖氨,這與常規(guī)的第三方對(duì)接模式很相似会烙,本地服務(wù)在請(qǐng)求第三方服務(wù)時(shí),如果請(qǐng)求過(guò)程耗時(shí)很大双泪,會(huì)異步執(zhí)行持搜,第三方第一次回調(diào),確認(rèn)請(qǐng)求可以被執(zhí)行焙矛;第二次回調(diào)則是推送處理結(jié)果葫盼,這種思想在處理復(fù)雜問(wèn)題時(shí),可以很大程度的提高性能村斟,節(jié)省資源:

異步模式對(duì)于性能的提升是巨大的贫导,當(dāng)然其相應(yīng)的處理機(jī)制也更復(fù)雜,程序的迭代和優(yōu)化是無(wú)止境的蟆盹,在 NIO 模式中再次對(duì) IO 流模式進(jìn)行優(yōu)化孩灯。

三、File 文件類(lèi)

1逾滥、基礎(chǔ)描述

File 類(lèi)作為文件和目錄路徑名的抽象表示峰档,用來(lái)獲取磁盤(pán)文件的相關(guān)元數(shù)據(jù)信息,例如:文件名稱(chēng)、大小讥巡、修改時(shí)間掀亩、權(quán)限判斷等。

注意:File 并不操作文件承載的數(shù)據(jù)內(nèi)容欢顷,文件內(nèi)容稱(chēng)為數(shù)據(jù)槽棍,文件自身信息稱(chēng)為元數(shù)據(jù)。

public class File01 {

? ? public static void main(String[] args) throws Exception {

? ? ? ? // 1抬驴、讀取指定文件

? ? ? ? File speFile = new File(IoParam.BASE_PATH+"fileio-03.text") ;

? ? ? ? if (!speFile.exists()){

? ? ? ? ? ? boolean creFlag = speFile.createNewFile() ;

? ? ? ? ? ? System.out.println("創(chuàng)建:"+speFile.getName()+"; 結(jié)果:"+creFlag);

? ? ? ? }

? ? ? ? // 2炼七、讀取指定位置

? ? ? ? File dirFile = new File(IoParam.BASE_PATH) ;

? ? ? ? // 判斷是否目錄

? ? ? ? boolean dirFlag = dirFile.isDirectory() ;

? ? ? ? if (dirFlag){

? ? ? ? ? ? File[] dirFiles = dirFile.listFiles() ;

? ? ? ? ? ? printFileArr(dirFiles);

? ? ? ? }

? ? ? ? // 3、刪除指定文件

? ? ? ? if (speFile.exists()){

? ? ? ? ? ? boolean delFlag = speFile.delete() ;

? ? ? ? ? ? System.out.println("刪除:"+speFile.getName()+"; 結(jié)果:"+delFlag);

? ? ? ? }

? ? }

? ? private static void printFileArr (File[] fileArr){

? ? ? ? if (fileArr != null && fileArr.length>0){

? ? ? ? ? ? for (File file : fileArr) {

? ? ? ? ? ? ? ? printFileInfo(file) ;

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? private static void printFileInfo (File file) {

? ? ? ? System.out.println("名稱(chēng):"+file.getName());

? ? ? ? System.out.println("長(zhǎng)度:"+file.length());

? ? ? ? System.out.println("路徑:"+file.getPath());

? ? ? ? System.out.println("文件判斷:"+file.isFile());

? ? ? ? System.out.println("目錄判斷:"+file.isDirectory());

? ? ? ? System.out.println("最后修改:"+new Date(file.lastModified()));

? ? ? ? System.out.println();

? ? }

}

上述案例使用了 File 類(lèi)中的基本構(gòu)造和常用方法(讀取布持、判斷豌拙、創(chuàng)建、刪除)等鳖链,JDK 源碼在不斷的更新迭代姆蘸,通過(guò)類(lèi)的構(gòu)造器、方法芙委、注釋等去判斷類(lèi)具有的基本功能逞敷,是作為開(kāi)發(fā)人員的必備能力。

在 File 文件類(lèi)中缺乏兩個(gè)關(guān)鍵信息描述:類(lèi)型和編碼灌侣,如果經(jīng)常開(kāi)發(fā)文件模塊的需求推捐,就知道這是兩個(gè)極其復(fù)雜的點(diǎn),很容易出現(xiàn)問(wèn)題侧啼,下面站在實(shí)際開(kāi)發(fā)的角度看看如何處理牛柒。

2、文件業(yè)務(wù)場(chǎng)景

如圖所示痊乾,在常規(guī)的文件流任務(wù)中皮壁,會(huì)涉及【文件、流哪审、數(shù)據(jù)】三種基本形式的轉(zhuǎn)換:

基本過(guò)程描述:

源文件生成蛾魄,推送文件中心;

通知業(yè)務(wù)使用節(jié)點(diǎn)獲取文件湿滓;

業(yè)務(wù)節(jié)點(diǎn)進(jìn)行邏輯處理滴须;

很顯然的一個(gè)問(wèn)題,任何節(jié)點(diǎn)都無(wú)法適配所有文件處理策略叽奥,比如類(lèi)型與編碼扔水,面對(duì)復(fù)雜場(chǎng)景下的問(wèn)題,規(guī)則約束是常用的解決策略朝氓,即在約定規(guī)則之內(nèi)的事情才處理魔市。

上面流程中主届,源文件節(jié)點(diǎn)通知業(yè)務(wù)節(jié)點(diǎn)時(shí)的數(shù)據(jù)主體描述:

public class BizFile {

? ? /**

? ? * 文件任務(wù)批次號(hào)

? ? */

? ? private String taskId ;

? ? /**

? ? * 是否壓縮

? ? */

? ? private Boolean zipFlag ;

? ? /**

? ? * 文件地址

? ? */

? ? private String fileUrl ;

? ? /**

? ? * 文件類(lèi)型

? ? */

? ? private String fileType ;

? ? /**

? ? * 文件編碼

? ? */

? ? private String fileCode ;

? ? /**

? ? * 業(yè)務(wù)關(guān)聯(lián):數(shù)據(jù)庫(kù)

? ? */

? ? private String bizDataBase ;

? ? /**

? ? * 業(yè)務(wù)關(guān)聯(lián):數(shù)據(jù)表

? ? */

? ? private String bizTableName ;

}

把整個(gè)過(guò)程當(dāng)做一個(gè)任務(wù)進(jìn)行封裝,即:任務(wù)批次待德、文件信息岂膳、業(yè)務(wù)庫(kù)表路由等,當(dāng)然這些信息也可以直接標(biāo)記在文件命名的策略上磅网,處理的手段類(lèi)似:

/**

* 基于約定策略讀取信息

*/

public class File02 {

? ? public static void main(String[] args) {

? ? ? ? BizFile bizFile = new BizFile("IN001",Boolean.FALSE, IoParam.BASE_PATH,

? ? ? ? ? ? ? ? "csv","utf8","model","score");

? ? ? ? bizFileInfo(bizFile) ;

? ? ? ? /*

? ? ? ? * 業(yè)務(wù)性校驗(yàn)

? ? ? ? */

? ? ? ? File file = new File(bizFile.getFileUrl());

? ? ? ? if (!file.getName().endsWith(bizFile.getFileType())){

? ? ? ? ? ? System.out.println(file.getName()+":描述錯(cuò)誤...");

? ? ? ? }

? ? }

? ? private static void bizFileInfo (BizFile bizFile){

? ? ? ? logInfo("任務(wù)ID",bizFile.getTaskId());

? ? ? ? logInfo("是否解壓",bizFile.getZipFlag());

? ? ? ? logInfo("文件地址",bizFile.getFileUrl());

? ? ? ? logInfo("文件類(lèi)型",bizFile.getFileType());

? ? ? ? logInfo("文件編碼",bizFile.getFileCode());

? ? ? ? logInfo("業(yè)務(wù)庫(kù)",bizFile.getBizDataBase());

? ? ? ? logInfo("業(yè)務(wù)表",bizFile.getBizTableName());

? ? }

}

基于主體描述的信息,也可以轉(zhuǎn)化到命名規(guī)則上:命名策略:編號(hào)_壓縮_Excel_編碼_庫(kù)_表筷屡,這樣一來(lái)在業(yè)務(wù)處理時(shí)涧偷,不符合約定的文件直接排除掉,降低文件異常導(dǎo)致的數(shù)據(jù)問(wèn)題毙死。

四燎潮、基礎(chǔ)流模式

1、整體概述

IO 流向

基本編碼邏輯:源文件->輸入流->邏輯處理->輸出流->目標(biāo)文件扼倘;

基于不同的角度看确封,流可以被劃分很多模式:

流動(dòng)方向:輸入流、輸出流再菊;

流數(shù)據(jù)類(lèi)型:字節(jié)流爪喘、字符流;

IO 流的模式有很多種纠拔,相應(yīng)的 API 設(shè)計(jì)也很復(fù)雜秉剑,通常復(fù)雜的 API 要把握住核心接口與常用的實(shí)現(xiàn)類(lèi)和原理。

基礎(chǔ) API

字節(jié)流:InputStream 輸入稠诲、OutputStream 輸出侦鹏;數(shù)據(jù)傳輸?shù)幕締挝皇亲止?jié);

read():輸入流中讀取數(shù)據(jù)的下一個(gè)字節(jié)臀叙;

read(byte b[]):讀數(shù)據(jù)緩存到字節(jié)數(shù)組略水;

write(int b):指定字節(jié)寫(xiě)入輸出流;

write(byte b[]):數(shù)組字節(jié)寫(xiě)入輸出流劝萤;


字符流:Reader 讀取渊涝、Writer 寫(xiě)出;數(shù)據(jù)傳輸?shù)幕締挝皇亲址?/p>

read():讀取一個(gè)單字符稳其;

read(char cbuf[]):讀取到字符數(shù)組驶赏;

write(int c):寫(xiě)一個(gè)指定字符;

write(char cbuf[]):寫(xiě)一個(gè)字符數(shù)組既鞠;

緩沖模式

IO 流常規(guī)讀寫(xiě)模式煤傍,即讀取到數(shù)據(jù)然后寫(xiě)出,還有一種緩沖模式嘱蛋,即數(shù)據(jù)先加載到緩沖數(shù)組蚯姆,在讀取的時(shí)候判斷是否要再次填充緩沖區(qū):

緩沖模式的優(yōu)點(diǎn)十分明顯五续,保證讀寫(xiě)過(guò)程的高效率,并且與數(shù)據(jù)填充過(guò)程隔離執(zhí)行龄恋,在 BufferedInputStream疙驾、BufferedReader 類(lèi)中是對(duì)緩沖邏輯的具體實(shí)現(xiàn)。

2郭毕、字節(jié)流

API 關(guān)系圖:

字節(jié)流基礎(chǔ) API:

public class IoByte01 {

? ? public static void main(String[] args) throws Exception {

? ? ? ? // 源文件 目標(biāo)文件

? ? ? ? File source = new File(IoParam.BASE_PATH+"fileio-01.png") ;

? ? ? ? File target = new File(IoParam.BASE_PATH+"copy-"+source.getName()) ;

? ? ? ? // 輸入流 輸出流

? ? ? ? InputStream inStream = new FileInputStream(source) ;

? ? ? ? OutputStream outStream = new FileOutputStream(target) ;

? ? ? ? // 讀入 寫(xiě)出

? ? ? ? byte[] byteArr = new byte[1024];

? ? ? ? int readSign ;

? ? ? ? while ((readSign=inStream.read(byteArr)) != -1){

? ? ? ? ? ? outStream.write(byteArr);

? ? ? ? }

? ? ? ? // 關(guān)閉輸入它碎、輸出流

? ? ? ? outStream.close();

? ? ? ? inStream.close();

? ? }

}

字節(jié)流緩沖 API:

public class IoByte02 {

? ? public static void main(String[] args) throws Exception {

? ? ? ? // 源文件 目標(biāo)文件

? ? ? ? File source = new File(IoParam.BASE_PATH+"fileio-02.png") ;

? ? ? ? File target = new File(IoParam.BASE_PATH+"backup-"+source.getName()) ;

? ? ? ? // 緩沖:輸入流 輸出流

? ? ? ? InputStream bufInStream = new BufferedInputStream(new FileInputStream(source));

? ? ? ? OutputStream bufOutStream = new BufferedOutputStream(new FileOutputStream(target));

? ? ? ? // 讀入 寫(xiě)出

? ? ? ? int readSign ;

? ? ? ? while ((readSign=bufInStream.read()) != -1){

? ? ? ? ? ? bufOutStream.write(readSign);

? ? ? ? }

? ? ? ? // 關(guān)閉輸入、輸出流

? ? ? ? bufOutStream.close();

? ? ? ? bufInStream.close();

? ? }

}

字節(jié)流應(yīng)用場(chǎng)景:數(shù)據(jù)是文件本身显押,例如圖片扳肛,視頻,音頻等乘碑。

3挖息、字符流

API 關(guān)系圖:

字符流基礎(chǔ) API:

public class IoChar01 {

? ? public static void main(String[] args) throws Exception {

? ? ? ? // 讀文本 寫(xiě)文本

? ? ? ? File readerFile = new File(IoParam.BASE_PATH+"io-text.txt") ;

? ? ? ? File writerFile = new File(IoParam.BASE_PATH+"copy-"+readerFile.getName()) ;

? ? ? ? // 字符輸入輸出流

? ? ? ? Reader reader = new FileReader(readerFile) ;

? ? ? ? Writer writer = new FileWriter(writerFile) ;

? ? ? ? // 字符讀入和寫(xiě)出

? ? ? ? int readSign ;

? ? ? ? while ((readSign = reader.read()) != -1){

? ? ? ? ? ? writer.write(readSign);

? ? ? ? }

? ? ? ? writer.flush();

? ? ? ? // 關(guān)閉流

? ? ? ? writer.close();

? ? ? ? reader.close();

? ? }

}

字符流緩沖 API:

public class IoChar02 {

? ? public static void main(String[] args) throws Exception {

? ? ? ? // 讀文本 寫(xiě)文本

? ? ? ? File readerFile = new File(IoParam.BASE_PATH+"io-text.txt") ;

? ? ? ? File writerFile = new File(IoParam.BASE_PATH+"line-"+readerFile.getName()) ;

? ? ? ? // 緩沖字符輸入輸出流

? ? ? ? BufferedReader bufReader = new BufferedReader(new FileReader(readerFile)) ;

? ? ? ? BufferedWriter bufWriter = new BufferedWriter(new FileWriter(writerFile)) ;

? ? ? ? // 字符讀入和寫(xiě)出

? ? ? ? String line;

? ? ? ? while ((line = bufReader.readLine()) != null){

? ? ? ? ? ? bufWriter.write(line);

? ? ? ? ? ? bufWriter.newLine();

? ? ? ? }

? ? ? ? bufWriter.flush();

? ? ? ? // 關(guān)閉流

? ? ? ? bufWriter.close();

? ? ? ? bufReader.close();

? ? }

}

字符流應(yīng)用場(chǎng)景:文件作為數(shù)據(jù)的載體,例如 Excel兽肤、CSV套腹、TXT 等。

4资铡、編碼解碼

編碼:字符轉(zhuǎn)換為字節(jié)电禀;

解碼:字節(jié)轉(zhuǎn)換為字符;

public class EnDeCode {

? ? public static void main(String[] args) throws Exception {

? ? ? ? String var = "IO流" ;

? ? ? ? // 編碼

? ? ? ? byte[] enVar = var.getBytes(StandardCharsets.UTF_8) ;

? ? ? ? for (byte encode:enVar){

? ? ? ? ? ? System.out.println(encode);

? ? ? ? }

? ? ? ? // 解碼

? ? ? ? String deVar = new String(enVar,StandardCharsets.UTF_8) ;

? ? ? ? System.out.println(deVar);

? ? ? ? // 亂碼

? ? ? ? String messyVar = new String(enVar,StandardCharsets.ISO_8859_1) ;

? ? ? ? System.out.println(messyVar);

? ? }

}

亂碼出現(xiàn)的根本原因害驹,就是在編碼與解碼的兩個(gè)階段使用的編碼類(lèi)型不同鞭呕。

5、序列化

序列化:對(duì)象轉(zhuǎn)換為流的過(guò)程宛官;

反序列化:流轉(zhuǎn)換為對(duì)象的過(guò)程葫松;

public class SerEntity implements Serializable {

? ? private Integer id ;

? ? private String name ;

}

public class Seriali01 {

? ? public static void main(String[] args) throws Exception {

? ? ? ? // 序列化對(duì)象

? ? ? ? OutputStream outStream = new FileOutputStream("SerEntity.txt") ;

? ? ? ? ObjectOutputStream objOutStream = new ObjectOutputStream(outStream);

? ? ? ? objOutStream.writeObject(new SerEntity(1,"Cicada"));

? ? ? ? objOutStream.close();

? ? ? ? // 反序列化對(duì)象

? ? ? ? InputStream inStream = new FileInputStream("SerEntity.txt");

? ? ? ? ObjectInputStream objInStream = new ObjectInputStream(inStream) ;

? ? ? ? SerEntity serEntity = (SerEntity) objInStream.readObject();

? ? ? ? System.out.println(serEntity);

? ? ? ? inStream.close();

? ? }

}

注意:引用類(lèi)型的成員對(duì)象也必須是可被序列化的,否則會(huì)拋出NotSerializableException異常底洗。

五腋么、NIO 模式

1、基礎(chǔ)概念

NIO 即(NonBlockingIO)亥揖,面向數(shù)據(jù)塊的處理機(jī)制珊擂,同步非阻塞模型,服務(wù)端的單個(gè)線(xiàn)程可以處理多個(gè)客戶(hù)端請(qǐng)求费变,對(duì) IO 流的處理速度有極高的提升摧扇,三大核心組件:

Buffer(緩沖區(qū)):底層維護(hù)數(shù)組存儲(chǔ)數(shù)據(jù);

Channel(通道):支持讀寫(xiě)雙向操作挚歧;

Selector(選擇器):提供 Channel 多注冊(cè)和輪詢(xún)能力扛稽;

API 使用案例

public class IoNew01 {

? ? public static void main(String[] args) throws Exception {

? ? ? ? // 源文件 目標(biāo)文件

? ? ? ? File source = new File(IoParam.BASE_PATH+"fileio-02.png") ;

? ? ? ? File target = new File(IoParam.BASE_PATH+"channel-"+source.getName()) ;

? ? ? ? // 輸入字節(jié)流通道

? ? ? ? FileInputStream inStream = new FileInputStream(source);

? ? ? ? FileChannel inChannel = inStream.getChannel();

? ? ? ? // 輸出字節(jié)流通道

? ? ? ? FileOutputStream outStream = new FileOutputStream(target);

? ? ? ? FileChannel outChannel = outStream.getChannel();

? ? ? ? // 直接通道復(fù)制

? ? ? ? // outChannel.transferFrom(inChannel, 0, inChannel.size());

? ? ? ? // 緩沖區(qū)讀寫(xiě)機(jī)制

? ? ? ? ByteBuffer buffer = ByteBuffer.allocateDirect(1024);

? ? ? ? while (true) {

? ? ? ? ? ? // 讀取通道中數(shù)據(jù)到緩沖區(qū)

? ? ? ? ? ? int in = inChannel.read(buffer);

? ? ? ? ? ? if (in == -1) {

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? }

? ? ? ? ? ? // 讀寫(xiě)切換

? ? ? ? ? ? buffer.flip();

? ? ? ? ? ? // 寫(xiě)出緩沖區(qū)數(shù)據(jù)

? ? ? ? ? ? outChannel.write(buffer);

? ? ? ? ? ? // 清空緩沖區(qū)

? ? ? ? ? ? buffer.clear();

? ? ? ? }

? ? ? ? outChannel.close();

? ? ? ? inChannel.close();

? ? }

}

上述案例只是 NIO 最基礎(chǔ)的文件復(fù)制能力,在網(wǎng)絡(luò)通信中滑负,NIO 模式的發(fā)揮空間十分寬廣在张。

2用含、網(wǎng)絡(luò)通信

服務(wù)端的單線(xiàn)程可以處理多個(gè)客戶(hù)端請(qǐng)求,通過(guò)輪詢(xún)多路復(fù)用器查看是否有 IO 請(qǐng)求帮匾,這樣一來(lái)啄骇,服務(wù)端的并發(fā)能力得到極大的提升,并且顯著降低了資源的消耗瘟斜。

API 案例:服務(wù)端模擬

public class SecServer {

? ? public static void main(String[] args) {

? ? ? ? try {

? ? ? ? ? ? //啟動(dòng)服務(wù)開(kāi)啟監(jiān)聽(tīng)

? ? ? ? ? ? ServerSocketChannel socketChannel = ServerSocketChannel.open();

? ? ? ? ? ? socketChannel.socket().bind(new InetSocketAddress("127.0.0.1", 8089));

? ? ? ? ? ? // 設(shè)置非阻塞缸夹,接受客戶(hù)端

? ? ? ? ? ? socketChannel.configureBlocking(false);

? ? ? ? ? ? // 打開(kāi)多路復(fù)用器

? ? ? ? ? ? Selector selector = Selector.open();

? ? ? ? ? ? // 服務(wù)端Socket注冊(cè)到多路復(fù)用器,指定興趣事件

? ? ? ? ? ? socketChannel.register(selector, SelectionKey.OP_ACCEPT);

? ? ? ? ? ? // 多路復(fù)用器輪詢(xún)

? ? ? ? ? ? ByteBuffer buffer = ByteBuffer.allocateDirect(1024);

? ? ? ? ? ? while (selector.select() > 0){

? ? ? ? ? ? ? ? Set<SelectionKey> selectionKeys = selector.selectedKeys();

? ? ? ? ? ? ? ? Iterator<SelectionKey> selectionKeyIter = selectionKeys.iterator();

? ? ? ? ? ? ? ? while (selectionKeyIter.hasNext()){

? ? ? ? ? ? ? ? ? ? SelectionKey selectionKey = selectionKeyIter.next() ;

? ? ? ? ? ? ? ? ? ? selectionKeyIter.remove();

? ? ? ? ? ? ? ? ? ? if(selectionKey.isAcceptable()) {

? ? ? ? ? ? ? ? ? ? ? ? // 接受新的連接

? ? ? ? ? ? ? ? ? ? ? ? SocketChannel client = socketChannel.accept();

? ? ? ? ? ? ? ? ? ? ? ? // 設(shè)置讀非阻塞

? ? ? ? ? ? ? ? ? ? ? ? client.configureBlocking(false);

? ? ? ? ? ? ? ? ? ? ? ? // 注冊(cè)到多路復(fù)用器

? ? ? ? ? ? ? ? ? ? ? ? client.register(selector, SelectionKey.OP_READ);

? ? ? ? ? ? ? ? ? ? } else if (selectionKey.isReadable()) {

? ? ? ? ? ? ? ? ? ? ? ? // 通道可讀

? ? ? ? ? ? ? ? ? ? ? ? SocketChannel client = (SocketChannel) selectionKey.channel();

? ? ? ? ? ? ? ? ? ? ? ? int len = client.read(buffer);

? ? ? ? ? ? ? ? ? ? ? ? if (len > 0){

? ? ? ? ? ? ? ? ? ? ? ? ? ? buffer.flip();

? ? ? ? ? ? ? ? ? ? ? ? ? ? byte[] readArr = new byte[buffer.limit()];

? ? ? ? ? ? ? ? ? ? ? ? ? ? buffer.get(readArr);

? ? ? ? ? ? ? ? ? ? ? ? ? ? System.out.println(client.socket().getPort() + "端口數(shù)據(jù):" + new String(readArr));

? ? ? ? ? ? ? ? ? ? ? ? ? ? buffer.clear();

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? }

}

API 案例:客戶(hù)端模擬

public class SecClient {

public static void main(String[] args) {

try {

// 連接服務(wù)端

SocketChannel socketChannel = SocketChannel.open();

socketChannel.connect(new InetSocketAddress("127.0.0.1", 8089));

ByteBuffer writeBuffer = ByteBuffer.allocate(1024);

String conVar = "[hello-8089]";

writeBuffer.put(conVar.getBytes()); writeBuffer.flip();

// 每隔5S發(fā)送一次數(shù)據(jù)

while (true) { Thread.sleep(5000);

writeBuffer.rewind();

socketChannel.write(writeBuffer);

writeBuffer.clear();

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

SelectionKey 綁定 Selector 和 Chanel 之間的關(guān)聯(lián)螺句,并且可以獲取就緒狀態(tài)下的 Channel 集合明未。

IO 流同系列文章:

|IO流概述|MinIO中間件|FastDFS中間件|Xml和CSV文件|Excel和PDF文件|文件上傳邏輯|

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市壹蔓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌猫态,老刑警劉巖佣蓉,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異亲雪,居然都是意外死亡勇凭,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)义辕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)虾标,“玉大人,你說(shuō)我怎么就攤上這事灌砖¤岛” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵基显,是天一觀的道長(zhǎng)蘸吓。 經(jīng)常有香客問(wèn)我,道長(zhǎng)撩幽,這世上最難降的妖魔是什么库继? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮窜醉,結(jié)果婚禮上宪萄,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好祟昭,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布壕探。 她就那樣靜靜地躺著,像睡著了一般聊记。 火紅的嫁衣襯著肌膚如雪撒妈。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天排监,我揣著相機(jī)與錄音狰右,去河邊找鬼。 笑死舆床,一個(gè)胖子當(dāng)著我的面吹牛棋蚌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播挨队,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼谷暮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了盛垦?” 一聲冷哼從身側(cè)響起湿弦,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎腾夯,沒(méi)想到半個(gè)月后颊埃,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蝶俱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年班利,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榨呆。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡罗标,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出积蜻,到底是詐尸還是另有隱情闯割,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布竿拆,位于F島的核電站纽谒,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏如输。R本人自食惡果不足惜鼓黔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望不见。 院中可真熱鬧澳化,春花似錦、人聲如沸稳吮。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)灶似。三九已至列林,卻和暖如春瑞你,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背希痴。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工者甲, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人砌创。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓虏缸,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親嫩实。 傳聞我的和親對(duì)象是個(gè)殘疾皇子刽辙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容