一文帶你你搞懂Java的3種IO模型_第1頁
一文帶你你搞懂Java的3種IO模型_第2頁
一文帶你你搞懂Java的3種IO模型_第3頁
一文帶你你搞懂Java的3種IO模型_第4頁
一文帶你你搞懂Java的3種IO模型_第5頁
已閱讀5頁,還剩13頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

第一文帶你你搞懂Java的3種IO模型目錄JavaBIOJavaNIOJavaAIO小結(jié)在Java中,一共有三種IO模型,分別是阻塞IO(BIO)、非阻塞IO(NIO)和異步IO(AIO)。

JavaBIO

JavaBIO就是Java的傳統(tǒng)IO模型,對應(yīng)了操作系統(tǒng)IO模型里的阻塞IO。

JavaBIO相關(guān)的實(shí)現(xiàn)都位于java.io包下,其通信原理是客戶端、服務(wù)端之間通過Socket套接字建立管道連接,然后從管道中獲取對應(yīng)的輸入/輸出流,最后利用輸入/輸出流對象實(shí)現(xiàn)發(fā)送/接收信息。

我們來看個Demo:

BioServer:

/**

*@Author三分惡

*@Date2025/4/30

*@DescriptionBIO服務(wù)端

publicclassBioServer{

publicstaticvoidmain(String[]args)throwsIOException{

//定義一個ServerSocket服務(wù)端對象,并為其綁定端口號

ServerSocketserver=newServerSocket(8888);

System.out.println("===========BIO服務(wù)端啟動================");

//對BIO來講,每個Socket都需要一個Thread

while(true){

//監(jiān)聽客戶端Socket連接

Socketsocket=server.accept();

newBioServerThread(socket).start();

*BIOServer線程

staticclassBioServerThreadextendsThread{

//socket連接

privateSocketsocket;

publicBioServerThread(Socketsocket){

this.socket=socket;

@Override

publicvoidrun(){

try{

//從socket中獲取輸入流

InputStreaminputStream=socket.getInputStream();

//轉(zhuǎn)換為

BufferedReaderbufferedReader=newBufferedReader(newInputStreamReader(inputStream));

Stringmsg;

//從Buffer中讀取信息,如果讀取到信息則輸出

while((msg=bufferedReader.readLine())!=null){

System.out.println("收到客戶端消息:"+msg);

//從socket中獲取輸出流

OutputStreamoutputStream=socket.getOutputStream();

PrintStreamprintStream=newPrintStream(outputStream);

//通過輸出流對象向客戶端傳遞信息

printStream.println("你好,吊毛!");

//清空輸出流

printStream.flush();

//關(guān)閉socket

socket.shutdownOutput();

}catch(IOExceptione){

e.printStackTrace();

}

BioClient

/**

*@Author三分惡

*@Date2025/4/30

*@DescriptionBIO客戶端

publicclassBioClient{

publicstaticvoidmain(String[]args)throwsIOException{

ListStringnames=Arrays.asList("帥哥","靚仔","坤坤");

//通過循環(huán)創(chuàng)建多個多個client

for(Stringname:names){

//創(chuàng)建socket并根據(jù)IP地址與端口連接服務(wù)端

Socketsocket=newSocket("127.0.0.1",8888);

System.out.println("===========BIO客戶端啟動================");

//從socket中獲取字節(jié)輸出流

OutputStreamoutputStream=socket.getOutputStream();

//通過輸出流向服務(wù)端傳遞信息

Stringhello="你好,"+name+"!";

outputStream.write(hello.getBytes());

//清空流,關(guān)閉socket輸出

outputStream.flush();

socket.shutdownOutput();

//從socket中獲取字節(jié)輸入流

InputStreaminputStream=socket.getInputStream();

BufferedReaderbufferedReader=newBufferedReader(newInputStreamReader(inputStream));

//讀取服務(wù)端消息

Stringmsg;

while((msg=bufferedReader.readLine())!=null){

System.out.println("收到服務(wù)端消息:"+msg);

inputStream.close();

outputStream.close();

socket.close();

}

先啟動BioServer,再啟動BioClient,運(yùn)行結(jié)果

===========BIO服務(wù)端啟動================

收到客戶端消息:你好,帥哥!

收到客戶端消息:你好,靚仔!

收到客戶端消息:你好,坤坤!

===========BIO客戶端啟動================

收到服務(wù)端消息:你好,吊毛!

===========BIO客戶端啟動================

收到服務(wù)端消息:你好,吊毛!

===========BIO客戶端啟動================

收到服務(wù)端消息:你好,吊毛!

在上述Java-BIO的通信過程中,如果客戶端一直沒有發(fā)送消息過來,服務(wù)端則會一直等待下去,從而服務(wù)端陷入阻塞狀態(tài)。同理,由于客戶端也一直在等待服務(wù)端的消息,如果服務(wù)端一直未響應(yīng)消息回來,客戶端也會陷入阻塞狀態(tài)。

在BioServer定義了一個類BioServerThread,繼承了Thread類,run方法里主要是通過socket和流來讀取客戶端的消息,以及發(fā)送消息給客戶端,每處理一個客戶端的Socket連接,就得新建一個線程。

同時,IO讀寫操作也是阻塞的,如果客戶端一直沒有發(fā)送消息過來,線程就會進(jìn)入阻塞狀態(tài),一直等待下去。

在BioClient里,循環(huán)創(chuàng)建Socket,向服務(wù)端收發(fā)消息,客戶端的讀寫也是阻塞的。

在這個Demo里就體現(xiàn)了BIO的兩個特點(diǎn):

一個客戶端連接對應(yīng)一個處理線程讀寫操作都是阻塞的

毫無疑問,不管是創(chuàng)建太多線程,還是阻塞讀寫,都會浪費(fèi)服務(wù)器的資源。

JavaNIO

那么我們就進(jìn)入Java的下一種IO模型JavaNIO,它對應(yīng)操作系統(tǒng)IO模型中的多路復(fù)用IO,底層采用了epoll實(shí)現(xiàn)。

Java-NIO則是JDK1.4中新引入的API,它在BIO功能的基礎(chǔ)上實(shí)現(xiàn)了非阻塞式的特性,其所有實(shí)現(xiàn)都位于java.nio包下。NIO是一種基于通道、面向緩沖區(qū)的IO操作,相較BIO而言,它能夠更為高效的對數(shù)據(jù)進(jìn)行讀寫操作,同時與原先的BIO使用方式也大有不同。

我們還是先來看個Demo:

NioServer

/**

*@Author三分惡

*@Date2025/4/30

*@DescriptionNIO服務(wù)端

publicclassNioServer{

publicstaticvoidmain(String[]args)throwsIOException{

//創(chuàng)建一個選擇器selector

Selectorselector=Selector.open();

//創(chuàng)建serverSocketChannel

ServerSocketChannelserverSocketChannel=ServerSocketChannel.open();

//綁定端口

serverSocketChannel.socket().bind(newInetSocketAddress(8888));

//必須得設(shè)置成非阻塞模式

serverSocketChannel.configureBlocking(false);

//將channel注冊到selector并設(shè)置監(jiān)聽事件為ACCEPT

serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);

System.out.println("===========NIO服務(wù)端啟動============");

while(true){

//超時等待

if(selector.select(1000)==0){

System.out.println("===========NIO服務(wù)端超時等待============");

continue;

//有客戶端請求被輪詢監(jiān)聽到,獲取返回的SelectionKey集合

IteratorSelectionKeyiterator=selector.selectedKeys().iterator();

//迭代器遍歷SelectionKey集合

while(iterator.hasNext()){

SelectionKeykey=iterator.next();

//判斷是否為ACCEPT事件

if(key.isAcceptable()){

//處理接收請求事件

SocketChannelsocketChannel=((ServerSocketChannel)key.channel()).accept();

//非阻塞模式

socketChannel.configureBlocking(false);

//注冊到Selector并設(shè)置監(jiān)聽事件為READ

socketChannel.register(selector,SelectionKey.OP_READ,ByteBuffer.allocate(1024));

System.out.println("成功連接客戶端");

//判斷是否為READ事件

if(key.isReadable()){

SocketChannelsocketChannel=(SocketChannel)key.channel();

try{

//獲取以前設(shè)置的附件對象,如果沒有則新建一個

ByteBufferbuffer=(ByteBuffer)key.attachment();

if(buffer==null){

buffer=ByteBuffer.allocate(1024);

key.attach(buffer);

//清空緩沖區(qū)

buffer.clear();

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

intlen=socketChannel.read(buffer);

if(len0){

buffer.flip();

Stringmessage=newString(buffer.array(),0,len);

System.out.println("收到客戶端消息:"+message);

}elseif(len0){

//接收到-1,表示連接已關(guān)閉

key.cancel();

socketChannel.close();

continue;

//注冊寫事件,下次向客戶端發(fā)送消息

socketChannel.register(selector,SelectionKey.OP_WRITE,buffer);

}catch(IOExceptione){

//取消SelectionKey并關(guān)閉對應(yīng)的SocketChannel

key.cancel();

socketChannel.close();

//判斷是否為WRITE事件

if(key.isWritable()){

SocketChannelsocketChannel=(SocketChannel)key.channel();

//獲取buffer

ByteBufferbuffer=(ByteBuffer)key.attachment();

Stringhello="你好,坤坤!";

//清空buffer

buffer.clear();

//buffer中寫入消息

buffer.put(hello.getBytes());

buffer.flip();

//向channel中寫入消息

socketChannel.write(buffer);

buffer.clear();

System.out.println("向客戶端發(fā)送消息:"+hello);

//設(shè)置下次讀寫操作,向Selector進(jìn)行注冊

socketChannel.register(selector,SelectionKey.OP_READ,buffer);

//移除本次處理的SelectionKey,防止重復(fù)處理

iterator.remove();

}

NioClient

publicclassNioClient{

publicstaticvoidmain(String[]args)throwsIOException{

//創(chuàng)建SocketChannel并指定ip地址和端口號

SocketChannelsocketChannel=SocketChannel.open(newInetSocketAddress("127.0.0.1",8888));

System.out.println("==============NIO客戶端啟動================");

//非阻塞模式

socketChannel.configureBlocking(false);

Stringhello="你好,靚仔!";

ByteBufferbuffer=ByteBuffer.wrap(hello.getBytes());

//向通道中寫入數(shù)據(jù)

socketChannel.write(buffer);

System.out.println("發(fā)送消息:"+hello);

buffer.clear();

//將channel注冊到Selector并監(jiān)聽READ事件

socketChannel.register(Selector.open(),SelectionKey.OP_READ,buffer);

while(true){

//讀取服務(wù)端數(shù)據(jù)

if(socketChannel.read(buffer)0){

buffer.flip();

Stringmsg=newString(buffer.array(),0,buffer.limit());

System.out.println("收到服務(wù)端消息:"+msg);

break;

//關(guān)閉輸入流

socketChannel.shutdownInput();

//關(guān)閉SocketChannel連接

socketChannel.close();

}

先運(yùn)行NioServer,再運(yùn)行NioClient,運(yùn)行結(jié)果:

===========NIO服務(wù)端啟動============

===========NIO服務(wù)端超時等待============

===========NIO服務(wù)端超時等待============

成功連接客戶端

收到客戶端消息:你好,靚仔!

向客戶端發(fā)送消息:你好?。?/p>

==============NIO客戶端啟動================

發(fā)送消息:你好,靚仔!

收到服務(wù)端消息:你好啊!

我們在這個案例里實(shí)現(xiàn)了一個比較簡單的JavaNIO客戶端服務(wù)端通信,里面有兩個小的點(diǎn)需要注意,注冊到選擇器上的通道都必須要為非阻塞模型,同時通過緩沖區(qū)傳輸數(shù)據(jù)時,必須要調(diào)用flip()方法切換為讀取模式。

Java-NIO中有三個核心概念:Buffer(緩沖區(qū))、Channel(通道)、Selector(選擇器)。

每個客戶端連連接本質(zhì)上對應(yīng)著一個Channel通道,每個通道都有自己的Buffer緩沖區(qū)來進(jìn)行讀寫,這些Channel被Selector選擇器管理調(diào)度

Selector負(fù)責(zé)輪詢所有已注冊的Channel,監(jiān)聽到有事件發(fā)生,才提交給服務(wù)端線程處理,服務(wù)端線程不需要做任何阻塞等待,直接在Buffer里處理Channel事件的數(shù)據(jù)即可,處理完馬上結(jié)束,或返回線程池供其他客戶端事件繼續(xù)使用。

通過Selector,服務(wù)端的一個Thread就可以處理多個客戶端的請求

Buffer(緩沖區(qū))就是飯店用來存放食材的儲藏室,當(dāng)服務(wù)員點(diǎn)餐時,需要從儲藏室中取出食材進(jìn)行制作。

Channel(通道)是用于傳輸數(shù)據(jù)的車道,就像飯店里的上菜窗口,可以快速把點(diǎn)好的菜品送到客人的桌上。

Selector(選擇器)就是大堂經(jīng)理,負(fù)責(zé)協(xié)調(diào)服務(wù)員、廚師和客人的配合和溝通,以保證整個就餐過程的效率和順暢。

JavaAIO

Java-AIO也被成為NIO2,它是在NIO的基礎(chǔ)上,引入了新的異步通道的概念,并提供了異步文件通道和異步套接字的實(shí)現(xiàn)。

它們的主要區(qū)別就在于這個異步通道,見名知意:使用異步通道去進(jìn)行IO操作時,所有操作都為異步非阻塞的,當(dāng)調(diào)用read()/write()/accept()/connect()方法時,本質(zhì)上都會交由操作系統(tǒng)去完成,比如要接收一個客戶端的數(shù)據(jù)時,操作系統(tǒng)會先將通道中可讀的數(shù)據(jù)先傳入read()回調(diào)方法指定的緩沖區(qū)中,然后再主動通知Java程序去處理。

我們還是先來看個Demo:

AioServer

/**

*@Author三分惡

*@Date2025/5/1

*@DescriptionAIO服務(wù)端

publicclassAioServer{

publicstaticvoidmain(String[]args)throwsException{

//創(chuàng)建異步通道組,處理IO事件

AsynchronousChannelGroupgroup=AsynchronousChannelGroup.withFixedThreadPool(10,Executors.defaultThreadFactory());

//創(chuàng)建異步服務(wù)器Socket通道,并綁定端口

AsynchronousServerSocketChannelserver=AsynchronousServerSocketChannel.open(group).bind(newInetSocketAddress(8888));

System.out.println("=============AIO服務(wù)端啟動=========");

//異步等待接收客戶端連接

server.accept(null,newCompletionHandlerAsynchronousSocketChannel,Object(){

//創(chuàng)建ByteBuffer

finalByteBufferbuffer=ByteBuffer.allocate(1024);

@Override

publicvoidcompleted(AsynchronousSocketChannelchannel,Objectattachment){

System.out.println("客戶端連接成功");

try{

buffer.clear();

//異步讀取客戶端發(fā)送的消息

channel.read(buffer,null,newCompletionHandlerInteger,Object(){

@Override

publicvoidcompleted(Integerlen,Objectattachment){

buffer.flip();

Stringmessage=newString(buffer.array(),0,len);

System.out.println("收到客戶端消息:"+message);

//異步發(fā)送消息給客戶端

channel.write(ByteBuffer.wrap(("你好,阿坤!").getBytes()),null,newCompletionHandlerInteger,Object(){

@Override

publicvoidcompleted(Integerresult,Objectattachment){

//關(guān)閉輸出流

try{

channel.shutdownOutput();

}catch(IOExceptione){

e.printStackTrace();

@Override

publicvoidfailed(Throwableexc,Objectattachment){

exc.printStackTrace();

try{

channel.close();

}catch(IOExceptione){

e.printStackTrace();

@Override

publicvoidfailed(Throwableexc,Objectattachment){

exc.printStackTrace();

try{

channel.close();

}catch(IOExceptione){

e.printStackTrace();

}catch(Exceptione){

e.printStackTrace();

//繼續(xù)異步等待接收客戶端連接

server.accept(null,this);

@Override

publicvoidfailed(Throwableexc,Objectattachment){

exc.printStackTrace();

//繼續(xù)異步等待接收客戶端連接

server.accept(null,this);

//等待所有連接都處理完畢

group.awaitTermination(Long.MAX_VALUE,TimeUnit.SECONDS);

}

AioClient

/**

*@Author三分惡

*@Date2025/5/1

*@DescriptionAIO客戶端

publicclassAioClient{

publicstaticvoidmain(String[]args)throwsException{

//創(chuàng)建異步Socket通道

AsynchronousSocketChannelclient=AsynchronousSocketChannel.open();

//異步連接服務(wù)器

client.connect(newInetSocketAddress("127.0.0.1",8888),null,newCompletionHandlerVoid,Object(){

//創(chuàng)建ByteBuffer

finalByteBufferbuffer=ByteBuffer.wrap(("你好,靚仔!").getBytes());

@Override

publicvoidcompleted(Voidresult,Objectattachment){

//異步發(fā)送消息給服務(wù)器

client.write(buffer,null,newCompletionHandlerInteger,Object(){

//創(chuàng)建ByteBuffer

finalByteBufferreadBuffer=ByteBuffer.allocate(1024);

@Override

publicvoidcompleted(Integerresult,Objectattachment){

readBuffer.clear();

//異步讀取服務(wù)器發(fā)送的消息

client.read(readBuffer,null,newCompletionHandlerInteger,Object(){

@Override

publicvoidcompleted(Integerresult,Objectattachment){

readBuffer.flip();

Stringmsg=newString(readBuffer.array(),0,result);

System.out.println("收到服務(wù)端消息:"+msg);

@Override

publicvoidfailed(Throwableexc,Objectattachment){

exc.printStackTrace();

try{

client.close();

}catch(IOExceptione){

e.printStackTrace();

@Override

publicvoidfailed(Throwableexc,Objectattachment){

exc.printS

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論