日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

BIO与NIO比较

發(fā)布時(shí)間:2025/5/22 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 BIO与NIO比较 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • BIO 同步阻塞
    • BIO介紹
    • BIO的編程流程
    • BIO實(shí)現(xiàn)通信
      • 實(shí)現(xiàn)思路:
      • 服務(wù)器:
      • 客戶端:
  • NIO 同步非阻塞
    • NIO中重要組件
      • channel:通道
      • Buffer緩沖區(qū)
        • 基本用法
        • Buffer實(shí)現(xiàn)原理
        • Buffer常見方法
        • Buffer的分配
      • selector:選擇器
        • Selector概述
        • selector的使用
    • NIO非阻塞式網(wǎng)絡(luò)通信原理分析
    • NIO實(shí)現(xiàn)
      • 服務(wù)端實(shí)現(xiàn)
      • 客戶端
  • BIO與AIO區(qū)別

BIO 同步阻塞

服務(wù)器實(shí)現(xiàn)模式為一個(gè)連接一個(gè)線程,即客戶端有連接請求時(shí)服務(wù)器端就需要啟動(dòng)
一個(gè)線程進(jìn)行處理,如果這個(gè)連接不做任何事情會(huì)造成不必要的線程開銷

BIO介紹

Java BIO 就是傳統(tǒng)的 java io 編程,其相關(guān)的類和接口在 java.ioBIO(blocking I/O) : 同步阻塞,服務(wù)器實(shí)現(xiàn)模式為一個(gè)連接一個(gè)線程,即客戶端有連接請求時(shí)服務(wù)器端就需要啟動(dòng)一個(gè)線程進(jìn)行處理,如果這個(gè)連接不做任何事情會(huì)造成不必要的線程開銷,可以通過線程池機(jī)制改善(實(shí)現(xiàn)多個(gè)客戶連接服務(wù)器).

BIO的編程流程

  • 服務(wù)器端啟動(dòng)一個(gè) ServerSocket,注冊端口,調(diào)用accpet方
    法監(jiān)聽客戶端的Socket連接。
  • 客戶端啟動(dòng) Socket對服務(wù)器進(jìn)行通信,默認(rèn)情況下服務(wù)器端
    需要對每個(gè)客戶 建立一個(gè)線程與之通訊
  • BIO實(shí)現(xiàn)通信

    通過BIO+線程池完成少量用戶的通信架構(gòu)

    實(shí)現(xiàn)思路:

    通過線程池控制解決為每個(gè)請求創(chuàng)建一個(gè)獨(dú)立線程造成線程資源耗盡的問題。
    存在的問題:
    但由于底層依然是采用的同步阻塞模型,因此無法從根本上解決問題。如果單個(gè)消息處理的緩慢,或者服務(wù)器線程池中的全部線程都被阻塞,那么后續(xù)socket的i/o消息都將在隊(duì)列中排隊(duì)。新的Socket請求將被拒絕,客戶端會(huì)發(fā)生大量連接超時(shí)。

    服務(wù)器:

    public class Server {public static void main(String[] args) {try {//注冊端口ServerSocket serverSocket = new ServerSocket(9999);//初始化一個(gè)線程對象HandlerSocketServerPool pool = new HandlerSocketServerPool(5, 20);//循環(huán)接受客戶端的請求while (true){Socket socket = serverSocket.accept();//將socket封裝成一個(gè)Runnable線程交給線程池ServerRunnableTarget runnable = new ServerRunnableTarget(socket);pool.execute(runnable);}} catch (Exception e) {e.printStackTrace();}} } //線程池類 class HandlerSocketServerPool{private ExecutorService executorService;//創(chuàng)建類的對象的時(shí)候初始化線程池對象public HandlerSocketServerPool(int maxThreadNum,int queuSize){executorService = new ThreadPoolExecutor(3, maxThreadNum, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queuSize));}/*** 提交一個(gè)方法來提交任務(wù)給線程池的任務(wù)隊(duì)列來暫時(shí)存儲,等著線程池的處理** */public void execute(Runnable target){executorService.execute(target);} } class ServerRunnableTarget implements Runnable{private Socket socket;public ServerRunnableTarget(Socket socket){this.socket=socket;}@Overridepublic void run() {//從Sacket得到一個(gè)字節(jié)輸入流try {InputStream inputStream = socket.getInputStream();//使用緩沖字符輸入流BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));String s;while ((s=bufferedReader.readLine())!=null){System.out.println(s);}} catch (Exception e) {e.printStackTrace();}} }

    客戶端:

    public class Client {public static void main(String[] args) throws IOException {System.out.println("客戶端啟動(dòng)");Socket socket = new Socket("127.0.0.1",9999);OutputStream outputStream = socket.getOutputStream();//打印流PrintStream printStream = new PrintStream(outputStream);Scanner scanner = new Scanner(System.in);while (true){System.out.println("請說:");String s = scanner.nextLine();printStream.println(s);printStream.flush();}} }

    NIO 同步非阻塞

    NIO中重要組件

    channel:通道

    channel和用戶操作IO相連,但通道的使用是不能直接訪問數(shù)據(jù)的需要和緩沖區(qū)Buffer相連
    讀數(shù)據(jù):將數(shù)據(jù)從channel中讀取到Buffer,從Buffer在獲取到數(shù)據(jù)
    寫數(shù)據(jù):將數(shù)據(jù)線寫入Buffer,Buffer中的數(shù)據(jù)寫入到通道

    channel與流stream的區(qū)別:

    channel不僅能讀,也能寫,stream通常是要么讀要么寫 channel可以同步也可以異步寫 channel總是讀取或?qū)懭胍粋€(gè)Buffer中

    主要的實(shí)現(xiàn)類:

    FileChannel:用于讀取、寫入、映射和操作文件的通道 DatagramChannel:通過UDP讀寫網(wǎng)絡(luò)中的數(shù)據(jù)通道 SocketChannel:通過TCP讀寫網(wǎng)絡(luò)中的數(shù)據(jù),一般是客戶端的實(shí)現(xiàn) ServerSocketChannel:監(jiān)聽新進(jìn)來的TCP連接,對每一個(gè)連接創(chuàng)建一個(gè)SocketChannel,一般是服務(wù)端的實(shí)現(xiàn)

    基于SocketChannel和ServerSocketChannel實(shí)現(xiàn)C/S大致流程:
    服務(wù)端
    1.通過ServerSocketChannel 綁定ip地址和端口號
    2.通過ServerSocketChannelImpl的accept()方法創(chuàng)建一個(gè)SocketChannel對象用戶從客戶端讀/寫數(shù)據(jù)
    3.創(chuàng)建讀數(shù)據(jù)/寫數(shù)據(jù)緩沖區(qū)對象來讀取客戶端數(shù)據(jù)或向客戶端發(fā)送數(shù)據(jù)
    4. 關(guān)閉SocketChannel和ServerSocketChannel
    Scatter / Gather( 散射/采集 )

    Scatter/Gather應(yīng)該使用直接的ByteBuffers以從本地I/O獲取最大性能優(yōu)勢。 Scatter/Gather功能是通道(Channel)提供的 并不是Buffer。 Scatter:從一個(gè)Channel讀取的信息分散到N個(gè)緩沖區(qū)中(Buufer). Gather:將N個(gè)Buffer里面內(nèi)容按照順序發(fā)送到一個(gè)Channel

    Buffer緩沖區(qū)

    Java NIO 的 Buffer 用于和 NIO Channel(通道)交互。數(shù)據(jù)是從通道讀入緩沖區(qū),從緩沖區(qū)寫入到通道中。緩沖區(qū)本質(zhì)上是塊可以寫入數(shù)據(jù),再從中讀數(shù)據(jù)的內(nèi)存。該內(nèi)存被包裝成 NIO 的 Buffer 對象,并提供了一系列方法,方便開發(fā)者訪問該塊內(nèi)存

    基本用法

    使用Buffer讀寫數(shù)據(jù)一般四步走:

    1、寫數(shù)據(jù)到 Buffer 2、調(diào)用buffer.flip切換為讀模式 3、從Buffer中讀取數(shù)據(jù) 4、調(diào)用clear()或者compact()清除數(shù)據(jù)

    當(dāng)向 buffer 寫數(shù)據(jù)時(shí),buffer 會(huì)記錄寫了多少數(shù)據(jù)。一旦要讀取數(shù)據(jù),需通過 flip() 將 Buffer 從寫模式切到讀模式。在讀模式下,可讀之前寫到 buffer 的所有數(shù)據(jù)。一旦讀完數(shù)據(jù),就需要清空緩沖區(qū),讓它可以再次被寫入。有兩種方式能清空緩沖區(qū):調(diào)用 clear() 或 compact() 方法。
    clear() 會(huì)清空整個(gè)緩沖區(qū)
    compact() 只會(huì)清除已經(jīng)讀過的數(shù)據(jù)。任何未讀數(shù)據(jù)都被移到緩沖區(qū)的起始處,新寫入的數(shù)據(jù)將放到緩沖區(qū)未讀數(shù)據(jù)的后面。

    Buffer實(shí)現(xiàn)原理

    Buffer就像一個(gè)數(shù)組,可以保存多個(gè)相同類型的數(shù)據(jù)。根據(jù)
    數(shù)據(jù)類型不同 ,有以下 Buffer 常用子類:

    ByteBuffer
    CharBuffer
    ShortBuffer
    IntBuffer
    LongBuffer
    FloatBuffer
    DoubleBuffer
    Buffer的實(shí)現(xiàn)底層是通過特定類型(byte、long…)數(shù)組來存儲數(shù)據(jù)
    數(shù)組中數(shù)據(jù)的操作需要借助4個(gè)指針來操作:

    private int mark = -1; //標(biāo)記 private int position = 0; //位置 private int limit; //限制 private int capacity; 容量 // Invariants: mark <= position <= limit <= capacity

    容量 (capacity) :作為一個(gè)內(nèi)存塊,Buffer具有一定的固定大小,
    也稱為"容量",緩沖區(qū)容量不能為負(fù),并且創(chuàng)建后不能更改。
    限制 (limit):表示緩沖區(qū)中可以操作數(shù)據(jù)的大小
    (limit 后數(shù)據(jù)不能進(jìn)行讀寫)。緩沖區(qū)的限制不能
    為負(fù),并且不能大于其容量。 寫入模式,限制等于
    buffer的容量。讀取模式下,limit等于寫入的數(shù)據(jù)量。

    位置 (position):下一個(gè)要讀取或?qū)懭氲臄?shù)據(jù)的索引。
    緩沖區(qū)的位置不能為 負(fù),并且不能大于其限制

    標(biāo)記 (mark)與重置 (reset):標(biāo)記是一個(gè)索引,
    通過 Buffer 中的 mark() 方法 指定 Buffer 中一個(gè)
    特定的 position,之后可以通過調(diào)用 reset() 方法恢
    復(fù)到這 個(gè) position.
    標(biāo)記、位置、限制、容量遵守以下不變式:
    0 <= mark <= position <= limit <= capacity

    Buffer常見方法

    Buffer clear() 清空緩沖區(qū)并返回對緩沖區(qū)的引用 Buffer flip() 為 將緩沖區(qū)的界限設(shè)置為當(dāng)前位置,并將當(dāng)前位置充值為 0 int capacity() 返回 Buffer 的 capacity 大小 boolean hasRemaining() 判斷緩沖區(qū)中是否還有元素 int limit() 返回 Buffer 的界限(limit) 的位置 Buffer limit(int n) 將設(shè)置緩沖區(qū)界限為 n,并返回一個(gè)具有新 limit 的緩沖區(qū)對象 Buffer mark() 對緩沖區(qū)設(shè)置標(biāo)記 int position() 返回緩沖區(qū)的當(dāng)前位置 position Buffer position(int n) 將設(shè)置緩沖區(qū)的當(dāng)前位置為 n,并返回修改后的 Buffer 對象 int remaining() 返回 position 和 limit 之間的元素個(gè)數(shù) Buffer reset() 將位置 position 轉(zhuǎn)到以前設(shè)置的mark 所在的位置 Buffer rewind() 將位置設(shè)為為 0, 取消設(shè)置的 mark

    Buffer的分配

    要想獲得一個(gè)Buffer對象首先要進(jìn)行分配。每個(gè)Buffer類都有一個(gè)allocate方法。
    直接與非直接緩沖區(qū)
    ByteBufferbyte byffer可以是兩種類型,一種是基于直接內(nèi)存(也就是
    非堆內(nèi)存);另一種是非直接內(nèi)存(也就是堆內(nèi)存)。對于直
    接內(nèi)存來說,JVM將會(huì)在IO操作上具有更高的性能,因?yàn)樗?br /> 直接作用于本地系統(tǒng)的IO操作。而非直接內(nèi)存,也就是堆內(nèi)
    存中的數(shù)據(jù),如果要作IO操作,會(huì)先從本進(jìn)程內(nèi)存復(fù)制到直接
    內(nèi)存,再利用本地IO處理。

    從數(shù)據(jù)流的角度,非直接內(nèi)存是下面這樣的作用鏈:
    本地IO–>直接內(nèi)存–>非直接內(nèi)存–>直接內(nèi)存–>本地IO

    而直接內(nèi)存是:
    本地IO–>直接內(nèi)存–>本地IO

    很明顯,在做IO處理時(shí),比如網(wǎng)絡(luò)發(fā)送大量數(shù)據(jù)時(shí),直接內(nèi)
    存會(huì)具有更高的效率。直接內(nèi)存使用allocateDirect創(chuàng)建,但
    是它比申請普通的堆內(nèi)存需要耗費(fèi)更高的性能。不過,這
    部分的數(shù)據(jù)是在JVM之外的,因此它不會(huì)占用應(yīng)用的內(nèi)
    存。所以呢,當(dāng)你有很大的數(shù)據(jù)要緩存,并且它的生命
    周期又很長,那么就比較適合使用直接內(nèi)存。只是一般
    來說,如果不是能帶來很明顯的性能提升,還是推薦直接
    使用堆內(nèi)存。字節(jié)緩沖區(qū)是直接緩沖區(qū)還是非直接緩沖
    區(qū)可通過調(diào)用其 isDirect() 方法來確定。
    Buffer的創(chuàng)建:
    ByteBuffer為例:
    ByteBuffer allocate(int capacity):在堆上創(chuàng)建指定大小的緩沖
    ByteBuffer allocateDirect(int capacity):在堆外空間創(chuàng)建指定大小的緩沖
    ByteBuffer wrap(byte[] array):通過byte數(shù)組實(shí)例創(chuàng)建一個(gè)緩沖區(qū)
    ByteBuffer wrap(byte[] array, int offset, int length) 指定byte數(shù)據(jù)中的內(nèi)容寫入到一個(gè)新的緩沖區(qū)
    向Buffer寫數(shù)據(jù)
    寫數(shù)據(jù)到Buffer有兩種方式:
    1、從Channel寫到Buffer

    inChannel.read(buf);

    2、通過Buffer的put()方法寫到Buffer里

    buf.put(127);

    flip()方法:
    flip方法將Buffer從寫模式切換到讀模式。調(diào)用flip()方法會(huì)將position設(shè)回0,并將limit設(shè)置成之前position的值。換句話說,position現(xiàn)在用于標(biāo)記讀的位置,limit表示之前寫進(jìn)了多少個(gè)byte、char等 —— 現(xiàn)在能讀取多少個(gè)byte、char等。
    從Buffer讀數(shù)據(jù)
    兩種方式:
    1、從Buffer讀取數(shù)據(jù)到Channel。

    int bytesWritten = inChannel.write(buf);

    2、使用get()方法從Buffer中讀取數(shù)據(jù)。

    byte aByte = buf.get();

    get方法有很多版本,允許你以不同的方式從Buffer中讀取數(shù)據(jù)。例如,從指定position讀取,或者從Buffer中讀取數(shù)據(jù)到字節(jié)數(shù)組。
    mark()與reset()方法
    通過調(diào)用Buffer.mark()方法,可以標(biāo)記Buffer中的一個(gè)特定position。之后可以通過調(diào)用Buffer.reset()方法恢復(fù)到這個(gè)position。例如:

    buffer.mark();// call buffer.get() a couple of times, e.g. during parsing.buffer.reset();

    selector:選擇器

    Selector概述

    選擇器(Selector) 是 SelectableChannle 對象的多路復(fù)用器,Selector 可以同時(shí)監(jiān)控多個(gè) SelectableChannel 的 IO 狀況,也就是說,利用 Selector可使一個(gè)單獨(dú)的線程管理多個(gè) Channel。Selector 是非阻塞 IO 的核心

    Java 的 NIO,用非阻塞的 IO 方式??梢杂靡粋€(gè)線程,處理多個(gè)的客戶端連接,就會(huì)使用到 Selector(選擇器)Selector 能夠檢測多個(gè)注冊的通道上是否有事件發(fā)生(注意:多個(gè) Channel 以事件的方式可以注冊到同一個(gè)Selector),如果有事件發(fā)生,便獲取事件然后針對每個(gè)事件進(jìn)行相應(yīng)的處理。這樣就可以只用一個(gè)單線程去管理多個(gè)通道,也就是管理多個(gè)連接和請求。只有在 連接/通道 真正有讀寫事件發(fā)生時(shí),才會(huì)進(jìn)行讀寫,就大大地減少了系統(tǒng)開銷,且不必為每個(gè)連接都創(chuàng)建一個(gè)線程,不用去維護(hù)多個(gè)線程避免了多線程之間的上下文切換導(dǎo)致的開銷
    selector優(yōu)勢:

    使用更少的線程管理更多的通道了,相比多線程,減少了上下文切換

    selector的使用

    創(chuàng)建 Selector :
    通過調(diào)用 Selector.open() 方法創(chuàng)建一個(gè) Selector。
    向選擇器注冊通道:
    SelectableChannel.register(Selector sel, int ops)

    舉例:

    //1. 獲取通道 ServerSocketChannel ssChannel = ServerSocketChannel.open(); //2. 切換非阻塞模式 ssChannel.configureBlocking(false); //3. 綁定連接 ssChannel.bind(new InetSocketAddress(9898)); //4. 獲取選擇器 Selector selector = Selector.open(); //5. 將通道注冊到選擇器上, 并且指定“監(jiān)聽接收事件” ssChannel.register(selector, SelectionKey.OP_ACCEPT);

    當(dāng)調(diào)用 register(Selector sel, int ops) 將通道注冊選擇
    器時(shí),選擇器對通道的監(jiān)聽事件,需要通過第二個(gè)參
    數(shù) ops 指定??梢员O(jiān)聽的事件類型(用 可使
    用 SelectionKey 的四個(gè)常量 表示):

    讀 : SelectionKey.OP_READ (1)
    寫 : SelectionKey.OP_WRITE (4)
    連接 : SelectionKey.OP_CONNECT (8)
    接收 : SelectionKey.OP_ACCEPT (16)
    若注冊時(shí)不止監(jiān)聽一個(gè)事件,則可以使用“位或”操作符連接。
    如:

    int interestSet = SelectionKey.OP_READ|SelectionKey.OP_WRITE

    NIO非阻塞式網(wǎng)絡(luò)通信原理分析

    Selector可以實(shí)現(xiàn): 一個(gè) I/O 線程可以并發(fā)處理 N 個(gè)客戶端連接和讀寫操作,這從根本上解決了傳統(tǒng)同步阻塞 I/O 一連接一線程模型,架構(gòu)的性能、彈性伸縮能力和可靠性都得到了極大的提升。

    NIO實(shí)現(xiàn)

    編寫一個(gè) NIO 群聊系統(tǒng),實(shí)現(xiàn)客戶端與客戶端的通信需求(非阻塞)
    服務(wù)器端:可以監(jiān)測用戶上線,離線,并實(shí)現(xiàn)消息轉(zhuǎn)發(fā)功能
    客戶端:通過 channel 可以無阻塞發(fā)送消息給其它所有客戶端用戶,同時(shí)可以接受其它客戶端用戶通過服務(wù)端轉(zhuǎn)發(fā)來的消息

    服務(wù)端實(shí)現(xiàn)

    //服務(wù)端群聊系統(tǒng)實(shí)現(xiàn) public class Server {//定義一些成員屬性:選擇器 服務(wù)器通道 端口private Selector selector;private ServerSocketChannel serverSocketChannel;private static final int PORT = 9999;//定義初始化代碼邏輯public Server(){//接受選擇器try {//初始化選擇器selector = Selector.open();//初始化通道serverSocketChannel=ServerSocketChannel.open();//綁定端口serverSocketChannel.bind(new InetSocketAddress(PORT));//通道切換為非阻塞模式serverSocketChannel.configureBlocking(false);//將通道注冊到選擇器上,并且開始指定監(jiān)聽接收事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);} catch (IOException e) {e.printStackTrace();}}/*** 監(jiān)聽客戶端各種消息事件:連接、群聊、離線* */private void listen(){try {//循環(huán)判斷是否存在就緒事件while (selector.select()>0){//獲取選擇器中的所有注冊的通道中已經(jīng)就緒好的事件Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();//開始遍歷這些準(zhǔn)備好的事件while (iterator.hasNext()){//提取當(dāng)前事件SelectionKey sk = iterator.next();//判斷是否為可接收事件if (sk.isAcceptable()){//獲取當(dāng)前接入的客戶端通道SocketChannel socketChannel = serverSocketChannel.accept();//通道切換為非阻塞模式socketChannel.configureBlocking(false);//將本客戶端的通道注冊到選擇器上System.out.println(socketChannel.getRemoteAddress() + " 上線 ");socketChannel.register(selector,SelectionKey.OP_READ);}//判斷是否為可讀事件if(sk.isReadable()){//讀操作和轉(zhuǎn)發(fā)給其他客戶端readClientData(sk);}iterator.remove();//處理完畢移除當(dāng)前事件}}}catch (Exception e){e.printStackTrace();}}/*** 接收當(dāng)前客戶端發(fā)送的消息,并轉(zhuǎn)發(fā)給全部客戶端通道* */private void readClientData(SelectionKey sk){SocketChannel socketChannel = null;try {//取到該讀操作的通道socketChannel = (SocketChannel) sk.channel();//創(chuàng)建緩沖區(qū)對象開始接收客戶端發(fā)送的消息ByteBuffer buffer = ByteBuffer.allocate(1024);int count = socketChannel.read(buffer);if(count>0){//設(shè)置為讀模式buffer.flip();//提取讀取到的信息String msg = new String(buffer.array(),0,count);System.out.println("接收到了客戶端信息:"+msg);//返回給其他在線客戶端消息sendMsgToAllClient(msg,socketChannel);}}catch (Exception e){//該客戶斷開連接會(huì)拋出異常,異常發(fā)出下線通知try {System.out.println(socketChannel.getRemoteAddress()+"下線了");sk.channel();//關(guān)閉通道socketChannel.close();} catch (IOException ioException) {ioException.printStackTrace();}}}//發(fā)送消息給所有在線人private void sendMsgToAllClient(String msg,SocketChannel socketChannel) throws Exception{System.out.println("服務(wù)端開始轉(zhuǎn)發(fā)消息,當(dāng)前處理的線程" + Thread.currentThread().getName());//循環(huán)給所有在線通道發(fā)送消息for (SelectionKey key:selector.keys()){Channel channel = key.channel();//不要把數(shù)據(jù)發(fā)送服務(wù)器和自己if(channel instanceof SocketChannel && socketChannel!=channel){//將消息存儲到buffer緩存ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());//將緩存寫入到通道( (SocketChannel)channel).write(buffer);}}}public static void main(String[] args) {//創(chuàng)建服務(wù)端對象Server server = new Server();//開始監(jiān)聽客戶端各種消息事件:連接、群聊、離線server.listen();} }

    客戶端

    //客戶端群聊系統(tǒng)實(shí)現(xiàn) public class Client {private Selector selector;private static final int PDRT = 9999;private SocketChannel socketChannel;public Client(){try {//初始化選擇器selector = Selector.open();//初始化通道,并綁定通信地址與端口socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",PDRT));//通道設(shè)置為非阻塞模式socketChannel.configureBlocking(false);//將通道加載到選擇器上,并開始指定監(jiān)聽讀事件socketChannel.register(selector, SelectionKey.OP_READ);System.out.println("當(dāng)前客戶端準(zhǔn)備完成");}catch (Exception e){e.printStackTrace();}}public static void main(String[] args) {Client client = new Client();//定義一個(gè)線程負(fù)責(zé)監(jiān)聽服務(wù)端發(fā)來的線程消息new Thread(new Runnable() {@Overridepublic void run() {try {while (true){//接收讀事件client.readInfo();}} catch (IOException e) {e.printStackTrace();}}}).start();//主線程進(jìn)行發(fā)送消息Scanner scanner = new Scanner(System.in);while (scanner.hasNextLine()){String s= scanner.nextLine();//數(shù)據(jù)發(fā)送client.sendTOServer(s);}}private void sendTOServer(String s) {try {//數(shù)據(jù)經(jīng)過緩存加載到通道上socketChannel.write(ByteBuffer.wrap(s.getBytes()));} catch (IOException e) {e.printStackTrace();}}/*** ** */private void readInfo() throws IOException {//判斷選擇器是是否有就緒事件if(selector.select()>0){//循環(huán)處理這些準(zhǔn)備就緒的事件Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()){//取得當(dāng)前就緒事件SelectionKey key = iterator.next();//判斷當(dāng)前是否為讀事件if(key.isReadable()){//取得當(dāng)前通道SocketChannel selectableChannel = (SocketChannel) key.channel();//創(chuàng)建buffer緩存接收事件ByteBuffer buffer = ByteBuffer.allocate(1024);//讀取通道上的數(shù)據(jù)socketChannel.read(buffer);//輸出緩存中數(shù)據(jù)System.out.println(new String(buffer.array()).trim());}//處理完關(guān)閉該通道iterator.remove();}}} }




    BIO與AIO區(qū)別

    BIO與NIO一個(gè)比較重要的不同,是我們使用BIO的時(shí)候往往會(huì)引入多線程,每個(gè)連接一個(gè)單獨(dú)的線程;
    而NIO則是使用單線程或者只使用少量的多線程,每個(gè)連接共用一個(gè)線程。NIO的最重要的地方是當(dāng)一個(gè)連接創(chuàng)建后,不需要對應(yīng)一個(gè)線程,這個(gè)連接會(huì)被注冊到多路復(fù)用器上面,所以所有的連接只需要一個(gè)線程就可以搞定,當(dāng)這個(gè)線程中的多路復(fù)用器進(jìn)行輪詢的時(shí)候,發(fā)現(xiàn)連接上有請求的話,才開啟一個(gè)線程進(jìn)行處理,也就是一個(gè)請求一個(gè)線程模式。
    NIO比BIO最大的好處是,一個(gè)線程可以處理多個(gè)socket(channel),這樣NIO+多線程會(huì)提高網(wǎng)絡(luò)服務(wù)器的性能,最主要是大大降低線程的數(shù)量
    服務(wù)器線程數(shù)量過多對系統(tǒng)有什么影響?
    1.java里面創(chuàng)建進(jìn)程和線程,最終映射到本地操作系統(tǒng)上創(chuàng)建進(jìn)程和線程,拿Linux來說,fork(進(jìn)程創(chuàng)建函數(shù))和pthread_create(線程創(chuàng)建函數(shù))都是重量級的函數(shù),調(diào)用它們開銷很大
    2.多線程隨著CPU的調(diào)度,會(huì)有上下文切換,如果線程過多,線程上下文切換的時(shí)間花費(fèi)慢慢趨近或者大于線程本身執(zhí)行指令的時(shí)間,那么CPU就完全被浪費(fèi)掉了,大大降低了系統(tǒng)的性能
    3.線程的開辟伴隨著線程私有內(nèi)存的分配,如果線程數(shù)量過多,為線程運(yùn)行準(zhǔn)備的內(nèi)存占去很多,真正能用來分配做業(yè)務(wù)處理的內(nèi)存大大減少,系統(tǒng)運(yùn)行不可靠
    4.數(shù)量過多的線程,阻塞等待網(wǎng)絡(luò)事件發(fā)生,如果一瞬間客戶請求量比較大,系統(tǒng)會(huì)瞬間喚醒很多數(shù)量的線程,造成系統(tǒng)瞬間的內(nèi)存使用率和CPU使用率居高不下,服務(wù)器系統(tǒng)不應(yīng)該總是出現(xiàn)鋸齒狀的系統(tǒng)負(fù)載,內(nèi)存使用率和CPU使用率應(yīng)該持續(xù)的保證平順運(yùn)行

    總結(jié)

    以上是生活随笔為你收集整理的BIO与NIO比较的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。