BIO(IO),NIO,AIO

目錄

# 同步?異步?阻塞?非阻塞?

# 同步IO和異步IO,阻塞IO和非阻塞IO  區別

# 概念

#BIO(IO)

#NIO

#AIO

 

 

同步?異步?阻塞?非阻塞?

同步:用戶進程觸發IO操作並等待或者輪詢的去查看IO操作是否就緒。

異步:用戶進程觸發IO操作以後便開始做自己的事情,而當IO操作已經完成的時候會得到IO完成的通知(異步的特點就是通知)

阻塞:當試圖對該文件描述符進行讀寫時, 如果當時沒有東西可讀,或者暫時不可寫, 程序就進入等待 狀態, 直到有東西可讀或者可寫爲止

非阻塞: 如果沒有東西可讀, 或者不可寫, 讀寫函數馬上返回, 而不會等待

綜上所述:

同步和異步是針對應用程序和內核的交互而言的,同步指的是用戶進程觸發IO操作並等待或者輪詢的去查看IO操作是否就緒,而異步是指用戶進程觸發IO操作以後便開始做自己的事情,而當IO操作已經完成的時候會得到IO完成的通知。

阻塞和非阻塞是針對於進程在訪問數據的時候,根據IO操作的就緒狀態來採取的不同方式,說白了是一種讀取或者寫入操作函數的實現方式,阻塞方式下讀取或者寫入函數將一直等待,而非阻塞方式下,讀取或者寫入函數會立即返回一個狀態值。

同步和異步是目的,阻塞和非阻塞是實現方式。

 

=======================================================================

 

同步IO和異步IO,阻塞IO和非阻塞IO  區別

一個IO操作其實分成了兩個步驟:發起IO請求 和 實際的IO操作。 

同步IO和異步IO的區別就在於第二個步驟是否阻塞,如果實際的IO讀寫阻塞請求進程,那麼就是同步IO

阻塞IO和非阻塞IO的區別在於第一步,發起IO請求是否會被阻塞,如果阻塞直到完成那麼就是傳統的阻塞IO,如果不阻塞,那麼就是非阻塞IO。 

 

=======================================================================

概念

一、同步阻塞I/O(BIO):jdk1.4以前  面向流

用戶進程在發起一個IO操作以後,必須等待IO操作的完成,只有當真正完成了IO操作以後,用戶進程才能運行。JAVA傳統的IO模型屬於此種方式。 

服務器實現模式爲一個連接一個線程,即客戶端有連接請求時服務器就需要啓動一個線程進行處理,如果這個連接不做任何事情會造成不必要的線程開銷,可以通過線程池機制來改善。

BIO方式架適用於連接數目比較小且固定的構,這種方式對服務端資源要求比較高,併發侷限於應用中。

 

二、同步非阻塞I/O(NIO):jdk1.4開始支持  面向緩衝區

用戶進程發起一個IO操作以後便可返回做其它事情,但是用戶進程需要時不時的詢問IO操作是否就緒,這就要求用戶進程不停的去詢問,從而引入不必要的CPU資源浪費。

服務器實現模式爲一個請求一個線程,即客戶端發送的連接請求都會註冊到多路複用器上,多路複用器輪詢到連接有IO請求時才啓動一個線程進行處理。

NIO方式適用於連接數目多且連接比較短(輕操作)的架構,比如聊天服務器,併發侷限於應用中,編程比較複雜。

 

三、異步非阻塞I/O(AIO):jdk1.7開始支持

應用發起一個IO操作以後,不等待內核IO操作的完成,等內核完成IO操作以後會通知應用程序。

服務器實現模式爲一個有效請求一個線程,客戶端的IO請求都是由操作系統先完成了再通知服務器用其啓動線程進行處理。

AIO方式適用於連接數目多且連接比較長(重操作)的架構,比如相冊服務器,充分調用OS參與併發操作,編程比較複雜。

 

 

喻:

目標:燒十壺開水。

IO:需要10個人,一個人盯一壺。

BIO:需要1個人,輪流查看幾個壺。

AIO:需要1個人兼職,給壺裝個開關,水開後提示,水未開時人可以去做任何事。

=======================================================================

BIO(IO)

Socket又稱“套接字”,應用程序通常通過“套接字”向網絡發出請求或者應答網絡請求。

Socket和ServerSocket類庫位於java.net包中,serverSocket用於服務器端,Socket是建立網絡連接時使用的。

在連接成功時,應用程序兩端都會產生一個Socket實例,操作這個實例,完成所需的會話。

對於一個網絡連接來說,套接字是平等的,不因爲在服務器端或在客戶端而產生不同的級別,

Socket和ServerSocket都是通過SocketImpl類及其子類完成的

缺點:該模型最大的問題就是,當客戶端數量增加時,服務端的線程數與客戶端併發的數量是 1:1 關係,我們知道在jvm 中線程是非常寶貴的資源,當線程數不斷上升時,系統性能將不斷下降,可能會出現堆棧溢出,創建線程失敗等問題,並最終導致宕機或者僵死,不對外提供服務。

 

網絡編程的基本模型Client/Server模型,也就是兩個進程直接進行相互通信,其中服務端提供配置信息(綁定的IP地址和監聽端口),客戶端通過連接操作向服務端監聽的地址發起連接請求,通過三次握手建立連接,如果連接成功,則雙方即可進行通信(網絡套接字socket)

 

優化:

採用線程池和任務隊列可以實現一種僞異步的IO通信框架。

我們學過連接池的使用和隊列的使用,其實就是將客戶端的socket封裝成一個task任務(實現runnable接口的類)然後投遞到線程池中去,配置相應的隊列進行實現。

代碼演示

package com.io.bio;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class ServerHandler implements Runnable {

    private Socket socket;

    public ServerHandler(Socket socket) {
        this.socket = socket;
    }

    public void run() {
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
            out = new PrintWriter(this.socket.getOutputStream(), true);
            String body =  null;
            while (true) {
                body =  in.readLine();
                if (body == null) {
                    break;
                }
                System.out.println("Server :"+ body);
                out.println("服務器端回送響的應數據.");
            }
        } catch (IOException e) {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            if (out != null) {
                out.close();
                out = null;
            }
            if (this.socket != null) {
                try {
                    this.socket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                this.socket = null;
            }
        }
    }
}
package com.io.bio;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) throws IOException {
        final int port = 8088;
        ServerSocket server = null;
        try {
            server = new ServerSocket(port);
            System.out.println("服務器端口號:" + port);
            Socket socket;
            while (true){
                //進行阻塞
                socket = server.accept();
                //新建一個線程執行客戶端的任務
                new Thread(new ServerHandler(socket)).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(server != null){
                System.out.println("The time server close");
                server.close();
                server = null;
            }
        }
    }
}
package com.io.bio;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class Client {
    public static void main(String[] args) {
        final int port = 8088;

        Socket socket = null;
        BufferedReader reader = null;
        PrintWriter writer = null;

        try {
            socket = new Socket("127.0.0.1", port);
        } catch (IOException e) {
            System.out.println("客戶端初始化失敗");
        }

        try {
            writer = new PrintWriter(socket.getOutputStream(),true);
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        } catch (IOException e) {
            System.out.println("輸入輸出流獲取失敗");
        }

        writer.println("客戶端向服務器端發送的請求");

        try {
            String readLine = reader.readLine();
            System.out.println("Client: " + readLine);
        } catch (IOException e) {
            System.out.println("讀取失敗");
        }finally {
            if(writer != null){
                writer.close();
                writer = null;
            }
            if(reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                reader = null;
            }
        }
    }
}

 

=======================================================================

NIO

NIO 叫做 new IO,也叫做 Non-block IO , 即非阻塞IO

傳統的IO流(inputstream,outputstream)都是單向的管道,要麼去讀,要麼去寫。

有一個server端,有n個client端。古老的socket編程,是每個客戶端直接向server端發起一個套接字,建立一個tcp連接。

NIO是在傳統的tcp之上進行一個抽象。不是client端和server端直接進行連接,而是,把client的通道註冊到server端。

在NIO中沒有Socket和ServerSocket的概念。

在NIO中服務端要實例化出一個ServerSocketChannel(把ServerSocket進行了一個抽象),客戶端使用SocketChannel。

在Server端會創建一個Selector(多路複用器)。所有客戶端的SocketChannel都要註冊到Selector。

Selector的機制是使用一個線程,去輪詢所有註冊到這個服務器端的SocketChannel,根據通道的狀態,執行相關的操作。
 

概念

(1)Buffer-緩衝區

(2)Channel(管道。通道)

(3)Selector(選擇器,多路複用器)

Buffer

是一個對象,它包含一些要寫入或者要讀取的數據。在NIO類庫中加入Buffer對象,體現了新庫與原IO的一個重要的區別。在面向流的IO中,可以將數據直接寫入或讀取到Stream對象中。在NIO庫中,所有的數據都是用緩衝區處理的(讀寫)。緩衝區實質上是一個數組,通常它是一個字節數組(ByteBuffer),也可以使用其他類型的數組,這個數組爲緩衝區提供了數據的訪問讀寫等操作屬性,如位置,容量,上限等概念,參考API文檔。Buffer類型:我們最常用的就是ByteBuffer,實際上每一種java基本類型都對應了一種緩衝區(除了Boolean類型)

ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBUffer、FloatBuffer、DoubleBUffer

 

使用:

flip()  將Buffer從寫模式切換到讀模式。調用flip()方法會將position設回0,並將limit設置成之前position的值。

get() 讀取數據

rewind() 將position設回0,所以你可以重讀Buffer中的所有數據。limit保持不變,仍然表示能從Buffer中讀取多少個元素

mark() 可以標記Buffer中的一個特定position。之後可以通過調用。

reset() 恢復到Buffer.mark()標記時的position。

clear() 清空整個緩衝區。position將被設回0,limit被設置成 capacity的值

compact() 只會清除已經讀過的數據;任何未讀的數據都被移到緩衝區的起始處,新寫入的數據將放到緩衝區未讀數據的後面。

   將position設到最後一個未讀元素正後面,limit被設置成 capacity的值。

這裏寫圖片描述

通道(Channel)

它就像自來水管道一樣,網絡數據通過Channel讀取和寫入,通道與流不同之處在於通道是雙向的,而流只是一個方向上移動(一個流必須是inputStream或者outputStream的子類),而通道可以用於讀,寫或者二者同時進行,最關鍵的是可以與多路複用器結合起來,有多種的狀態位,方便多路複用器去識別。

事實上通道分爲兩大類,一類是網絡讀寫的(SelectableChannel),一類是用於文件操作的(FileChannel),我們使用的SocketChannel和ServerSockerChannel都是SelectableChannel的子類

 

使用

FileChannel:用於讀取、寫入、映射和操作文件的通道。

DatagramChannel:通過 UDP 讀寫網絡中的數據通道。

SocketChannel:通過 TCP 讀寫網絡中的數據。

ServerSocketChannel:可以監聽新進來的 TCP 連接,對每一個新進來的連接都會創建一個 SocketChannel。

AbstractInterruptibleChannel

AbstractSelectableChannel

Pipe.SinkChannel

Pipe.SourceChannel

SelectableChannel

這裏寫圖片描述

多路複用器(seletor)

他是NIO編程的基礎,非常重要,多路複用器提供選擇已經就緒的任務的能力。

Selector線程就類似一個管理者(Master),管理了成千上萬個管道,然後輪詢哪個管道的數據已經準備好,通知CPU執行IO的讀取或寫入操作。

Selector模式:當IO事件(管理)註冊到選擇器以後,selector會分配給每個管道一個key值,相當於標籤。selector選擇器是以輪詢的方式進行查找註冊的所有IO事件(管道)

如果某個通道發生了讀寫操作,這個通道就處於就緒狀態,select就會識別,會通過key值來找到相應的管道,進行相關的數據處理操作(從管道里讀或寫數據,寫道我們的數據緩衝區中)。

 

每個管道都會對選擇器進行註冊不同的事件狀態,以便選擇器查找。

SelectionKey.OP_CONNECT         連接狀態

SelectionKey.OP_ACCEPT            阻塞狀態

SelectionKey.OP_READ                可讀狀態

SelectionKey.OP_WRITE               可寫狀態

創建NIO服務器的主要步驟

    (1)打開ServerSocketChannel,監聽客戶端連接

    (2)綁定監聽端口,設置連接爲非阻塞模式

    (3)創建Reactor線程,創建多路複用器並啓動線程

     (4)將ServerSocketChannel註冊到Reactor線程中的Selector上,監聽ACCEPT事件

     (5)Selector輪詢準備就緒的key

     (6)Selector監聽到新的客戶端接入,處理新的接入請求,完成TCP三次握手,簡歷物理鏈路

     (7)設置客戶端鏈路爲非阻塞模式

     (8)將新接入的客戶端連接註冊到Reactor線程的Selector上,監聽讀操作,讀取客戶端發送的網絡消息

     (9)異步讀取客戶端消息到緩衝區

     (10)對Buffer編解碼,處理半包消息,將解碼成功的消息封裝成Task

     (11)將應答消息編碼爲Buffer,調用SocketChannel的write將消息異步發送給客戶端

因爲應答消息的發送,SocketChannel也是異步非阻塞的,所以不能保證一次能吧需要發送的數據發送完,此時就會出現寫半包的問題。我們需要註冊寫操作,不斷輪詢Selector將沒有發送完的消息發送完畢,然後通過Buffer的hasRemain()方法判斷消息是否發送完成。

 

代碼演示

package com.io.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

public class Server implements Runnable {
    //多路複用器(管理所有的通道)
    private Selector seletor;
    //2 建立緩衝區
    private ByteBuffer readBuf = ByteBuffer.allocate(1024);
    //3
    private ByteBuffer writeBuf = ByteBuffer.allocate(1024);

    public Server(int port) {
        try {
            //步驟一: 打開服務器通道(ServerSocketChannel),用於監聽客戶端的連接,它是所有客戶端連接的副管道
            ServerSocketChannel socketChannel = ServerSocketChannel.open();

            //步驟二: 綁定監聽端口,設置連接爲非阻塞模式
            socketChannel.bind(new InetSocketAddress(port));
            socketChannel.configureBlocking(false);

            //步驟三: 創建多路複用器(Selector)
            this.seletor = Selector.open();

            //步驟四: 把服務器通道(ServerSocketChannel)註冊到多路複用器(Selector)上,並且監聽阻塞(ACCEPT)事件
            socketChannel.register(this.seletor, SelectionKey.OP_ACCEPT);

            System.out.println("Server start, port :" + port);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        //步驟五:多路複用器在線程run方法的無線循環體內輪詢準備就緒的key
        while (true) {
            try {
                //1 必須要讓多路複用器開始監聽
                this.seletor.select();
                //2 返回多路複用器已經選擇的結果集
                Iterator<SelectionKey> keys = this.seletor.selectedKeys().iterator();
                //3 進行遍歷
                while (keys.hasNext()) {
                    //4 獲取一個選擇的元素
                    SelectionKey key = keys.next();
                    //5 直接從容器中移除就可以了
                    keys.remove();
                    //6 如果是有效的
                    if (key.isValid()) {
                        //7 如果爲阻塞狀態
                        if (key.isAcceptable()) {
                            //步驟六:多路複用器監聽到有新的客戶端接入,處理新的接入請求,完成TCP三次握手,建立物理連接
                            //步驟七:設置客戶端鏈路爲非阻塞模式 3
                            //步驟八:將新接入的客戶端連接註冊到的多路複用器(Selector)上,監聽讀操作,讀取客戶端發送的網絡消息 4
                            this.accept(key);
                        }
                        //8 如果爲可讀狀態
                        if (key.isReadable()) {
                            //步驟九:異步讀取客戶端請求消息到緩衝區
                            this.read(key);
                        }
                        //9 寫數據
                        if (key.isWritable()) {
                            this.write(key);
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void write(SelectionKey key) {
        ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
        try {
            ssc.register(this.seletor, SelectionKey.OP_WRITE);
        } catch (ClosedChannelException e) {
            e.printStackTrace();
        }
    }

    private void read(SelectionKey key) {
        try {
            //1 清空緩衝區舊的數據
            this.readBuf.clear();
            //2 獲取之前註冊的socket通道對象
            SocketChannel sc = (SocketChannel) key.channel();
            //3 讀取數據
            int count = sc.read(this.readBuf);
            //4 如果沒有數據
            if (count == -1) {
                key.channel().close();
                key.cancel();
                return;
            }
            //5 有數據則進行讀取 讀取之前需要進行復位方法(把position 和limit進行復位)
            this.readBuf.flip();
            //6 根據緩衝區的數據長度創建相應大小的byte數組,接收緩衝區的數據
            byte[] bytes = new byte[this.readBuf.remaining()];
            //7 接收緩衝區數據
            this.readBuf.get(bytes);
            //8 打印結果
            String body = new String(bytes).trim();
            // 9.可以寫回給客戶端數據
            System.out.println("Server : " + body);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void accept(SelectionKey key) {
        try {
            //1 獲取服務通道
            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
            //2 執行阻塞方法
            SocketChannel sc = ssc.accept();
            //3 設置爲非阻塞模式
            sc.configureBlocking(false);
            sc.socket().setReuseAddress(true);
            //4 註冊到多路複用器上,並設置讀取標識
            sc.register(this.seletor, SelectionKey.OP_READ);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new Thread(new Server(8081)).start();
    }
}

 

package com.io.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class Client {

    //需要一個Selector
    public static void main(String[] args) {
        //創建連接的地址
        InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8081);
        //聲明連接通道
        SocketChannel sc = null;
        //建立緩衝區
        ByteBuffer buf = ByteBuffer.allocate(1024);
        try {
            //打開通道
            sc = SocketChannel.open();
            //進行連接
            sc.connect(address);
            while(true){
                //定義一個字節數組,然後使用系統錄入功能:
                byte[] bytes = new byte[1024];
                System.in.read(bytes);
                buf.put(bytes);//把數據放到緩衝區中
                buf.flip();//對緩衝區進行復位
                sc.write(buf);//寫出數據
                buf.clear();//清空緩衝區數據
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(sc != null){
                try {
                    sc.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 

推薦NIO底層講解網址:

深入淺出NIO之Channel、Buffer

深入淺出NIO之Selector實現原理

 

=======================================================================

AIO

package com.io.aio;

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutionException;

public class ServerHandler implements CompletionHandler<AsynchronousSocketChannel, Server> {

    @Override
    public void completed(AsynchronousSocketChannel asc, Server attachment) {
        //當有下一個客戶端接入的時候 直接調用Server的accept方法,這樣反覆執行下去,保證多個客戶端都可以阻塞(沒有遞歸上限)
        // 1.7以後AIO才實現了異步非阻塞
        attachment.assc.accept(attachment, this);
        read(asc);
    }

    private void read(final AsynchronousSocketChannel asc) {
        //讀取數據
        ByteBuffer buf = ByteBuffer.allocate(1024);
        asc.read(buf, buf, new CompletionHandler<Integer, ByteBuffer>() {
            @Override
            public void completed(Integer resultSize, ByteBuffer attachment) {
                //進行讀取之後,重置標識位
                attachment.flip();
                //獲得讀取的字節數
                System.out.println("Server -> " + "收到客戶端的數據長度爲:" + resultSize);
                //獲取讀取的數據
                String resultData = new String(attachment.array()).trim();
                System.out.println("Server -> " + "收到客戶端的數據信息爲:" + resultData);
                String response = "服務器響應, 收到了客戶端發來的數據: " + resultData;
                write(asc, response);
            }
            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {
                exc.printStackTrace();
            }
        });
    }

    private void write(AsynchronousSocketChannel asc, String response) {
        try {
            ByteBuffer buf = ByteBuffer.allocate(1024);
            buf.put(response.getBytes());
            buf.flip();
            asc.write(buf).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void failed(Throwable exc, Server attachment) {
        exc.printStackTrace();
    }
}
package com.io.aio;

import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Server{
    //線程池
    private ExecutorService executorService;
    //線程組
    private AsynchronousChannelGroup threadGroup;
    //服務器通道
    public AsynchronousServerSocketChannel assc;

    public Server(int port){
        try {
            //創建一個緩存池
            executorService = Executors.newCachedThreadPool();
            //創建線程組
            threadGroup = AsynchronousChannelGroup.withCachedThreadPool(executorService, 1);
            //創建服務器通道
            assc = AsynchronousServerSocketChannel.open(threadGroup);
            //進行綁定
            assc.bind(new InetSocketAddress(port));
            System.out.println("server start , port : " + port);
            //進行阻塞
            assc.accept(this, new ServerHandler());
            //一直阻塞 不讓服務器停止
            Thread.sleep(Integer.MAX_VALUE);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Server server = new Server(8082);
    }
}
package com.io.aio;

import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.ExecutionException;

public class Client implements Runnable{

    private AsynchronousSocketChannel asc ;

    public Client() throws Exception {
        asc = AsynchronousSocketChannel.open();
    }

    public void connect(){
        asc.connect(new InetSocketAddress("127.0.0.1", 8082));
    }

    public void write(String request){
        try {
            asc.write(ByteBuffer.wrap(request.getBytes())).get();
            read();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void read() {
        ByteBuffer buf = ByteBuffer.allocate(1024);
        try {
            asc.read(buf).get();
            buf.flip();
            byte[] respByte = new byte[buf.remaining()];
            buf.get(respByte);
            System.out.println(new String(respByte,"utf-8").trim());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while(true){

        }
    }

    public static void main(String[] args) throws Exception {
        Client c1 = new Client();
        c1.connect();
        Client c2 = new Client();
        c2.connect();
        Client c3 = new Client();
        c3.connect();
        new Thread(c1, "c1").start();
        new Thread(c2, "c2").start();
        new Thread(c3, "c3").start();
        Thread.sleep(1000);
        c1.write("c1 aaa");
        c2.write("c2 bbbb");
        c3.write("c3 ccccc");
    }
}

=======================================================================

參考地址

BIO、NIO、AIO的深度解析於區別

尚硅谷java學習筆記——NIO(New IO)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章