最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java網(wǎng)絡(luò)IO模型詳解(BIO、NIO、AIO)

 更新時間:2024年10月09日 08:46:28   作者:沈小洋  
Java支持BIO、NIO和AIO三種網(wǎng)絡(luò)IO模型,BIO是同步阻塞模型,適用于連接數(shù)較少的場景,NIO是同步非阻塞模型,適用于處理多個連接,支持自JDK1.4起,AIO是異步非阻塞模型,適用于異步操作多的場景,文中通過代碼介紹的非常詳細,需要的朋友可以參考下

簡介

Java 支持三種網(wǎng)絡(luò) IO 模型:BIO、NIO、AIO。

  • Java BIO 是同步阻塞模型,一個連接對應(yīng)一個線程,客戶端有連接請求時服務(wù)端就啟動一個線程,即使這個連接不做任何事情也會占用線程資源。
  • Java NIO 是同步非阻塞模型,一個線程可以處理多個連接,客戶端連接請求會注冊到多路復(fù)用器(Selector),多路復(fù)用器檢測到連接有 IO 時間就會處理。
  • Java AIO 是異步非阻塞模型,AIO 引入了異步通道的概念,讀寫異步通道會立刻返回,讀寫的數(shù)據(jù)由 Future 或 CompletionHandler 進一步處理。

BIO 適用于連接數(shù)少的場景,程序編寫比較簡單,對服務(wù)器的資源要求比較高,JDK1.4之前的唯一選擇。NIO 適用于連接數(shù)多的場景,例如聊天服務(wù)器、服務(wù)器間通訊等,程序編寫比較復(fù)雜,JDK1.4開始支持。AIO 也適用于連接數(shù)多的場景,但更加偏向于異步操作多的場景。

Java BIO

模型示例

客戶端代碼示例

import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

public class BIOClient {
    public static void main(String[] args) {
        new BIOClient().start("localhost", 6666);
    }

    public void start(String host, int port) {
        // 初始化 socket
        Socket socket = new Socket();

        try {
            // 設(shè)置 socket 連接
            SocketAddress remote = new InetSocketAddress(host, port);
            socket.setSoTimeout(5000);
            socket.connect(remote);

            // 發(fā)送數(shù)據(jù)
            PrintWriter writer = getWriter(socket);
            writer.write("hello server");
            writer.flush();

//            // 發(fā)起請求
//            PrintWriter writer = getWriter(socket);
//            writer.write(compositeRequest(host));
//            writer.flush();
//
//            // 讀取響應(yīng)
//            String msg;
//            BufferedReader reader = getReader(socket);
//            while ((msg = reader.readLine()) != null) {
//                System.out.println(msg);
//            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private BufferedReader getReader(Socket socket) throws IOException {
        InputStream in = socket.getInputStream();
        return new BufferedReader(new InputStreamReader(in));
    }

    private PrintWriter getWriter(Socket socket) throws IOException {
        OutputStream out = socket.getOutputStream();
        return new PrintWriter(new OutputStreamWriter(out));
    }

    private String compositeRequest(String host) {
        return "GET / HTTP/1.1\r\n" +
                "Host: " + host + "\r\n" +
                "User-Agent: curl/7.43.0\r\n" +
                "Accept: */*\r\n\r\n";
    }
}

服務(wù)端代碼示例

import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class BIOServer {
    public static void main(String[] args) throws Exception {
        // 創(chuàng)建一個線程池
        ExecutorService pool = Executors.newCachedThreadPool();
        // 創(chuàng)建 ServerSocket
        ServerSocket serverSocket = new ServerSocket(6666);
        while (true) {
            // 等待客戶端連接
            final Socket socket = serverSocket.accept();
            // 接收到一個客戶端連接 放入線程池進行處理
            pool.execute(() -> process(socket));
        }
    }

    static void process(Socket socket) {
        try {
            byte[] bytes = new byte[1024];
            // 通過 socket 獲取輸入流
            InputStream inputStream = socket.getInputStream();
            // 循環(huán)讀取客戶端發(fā)送的數(shù)據(jù)
            while (true) {
                // 沒有數(shù)據(jù)的時候這里會阻塞等待
                int read = inputStream.read(bytes);
                if (read == -1) break;
                // 輸出客戶端發(fā)送的數(shù)據(jù)
                System.out.println(new String(bytes, 0, read));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

Java NIO

NIO 采用 Reactor 模式,屬于 IO 多路復(fù)用模型,可以用一個線程處理多個請求。NIO 有三大核心模塊,通道(Channel)、緩沖區(qū)(Buffer)、選擇器(Selector)。NIO 的非阻塞模式,使主線程在未發(fā)生數(shù)據(jù)讀寫事件時無需阻塞,可以繼續(xù)做其他事情,這就大大增強了服務(wù)器的并發(fā)處理能力。

模型示例

Selector 對應(yīng)一個線程,一個 Selector 可以對應(yīng)多個 Channel,一個 Channel 對應(yīng)一個 Buffer。程序切換到哪個 Channel 是由事件決定的,Selector 會根據(jù)不同的事件切換不同的 Channel。下圖描述了 Channel、Buffer 和 Selector 的關(guān)系。

MappedByteBuffer 簡介

NIO 提供的 MappedByteBuffer 支持支持在內(nèi)存(堆外內(nèi)存)中修改文件,可以減少一次數(shù)據(jù)拷貝。文件同步的部分,由 NIO 自己完成。

代碼示例

import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * 說明 1.MappedByteBuffer 可讓文件直接在內(nèi)存(堆外內(nèi)存)修改,操作系統(tǒng)不需要拷貝一次
 */
public class MappedByteBufferTest {

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

        RandomAccessFile randomAccessFile = new RandomAccessFile("1.txt", "rw");
        //獲取對應(yīng)的通道
        FileChannel channel = randomAccessFile.getChannel();

        /**
         * 參數(shù) 1:FileChannel.MapMode.READ_WRITE 使用的讀寫模式
         * 參數(shù) 2:0:可以直接修改的起始位置
         * 參數(shù) 3:5: 是映射到內(nèi)存的大?。ú皇撬饕恢茫磳?1.txt 的多少個字節(jié)映射到內(nèi)存
         * 可以直接修改的范圍就是 0-5
         * 實際類型 DirectByteBuffer
         */
        MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);

        mappedByteBuffer.put(0, (byte) 'H');
        mappedByteBuffer.put(3, (byte) '9');
        mappedByteBuffer.put(5, (byte) 'Y');//IndexOutOfBoundsException

        randomAccessFile.close();
        System.out.println("修改成功~~");
    }
}

 NIO 編程代碼原理分析圖

關(guān)于 NIO 非阻塞網(wǎng)絡(luò)編程相關(guān)的(Selector、SelectionKey、ServerScoketChannel 和 SocketChannel)關(guān)系梳理圖

服務(wù)端代碼示例

可以結(jié)合上面的原理圖觀察代碼實現(xiàn)細節(jié)

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

public class GroupChatServer {

    // 定義屬性
    private Selector selector;
    private ServerSocketChannel listenChannel;

    private static final int PORT = 6667;

    // 構(gòu)造器執(zhí)行初始化工作
    public GroupChatServer() {
        try {
            // 得到選擇器
            selector = Selector.open();
            // 監(jiān)聽端口的主線程
            listenChannel = ServerSocketChannel.open();
            // 綁定端口
            listenChannel.socket().bind(new InetSocketAddress(PORT));
            // 設(shè)置非阻塞模式
            listenChannel.configureBlocking(false);
            // 將該 listenChannel 注冊到 selector
            listenChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void listen() {
        try {
            // 循環(huán)處理
            while (true) {
                int count = selector.select();
                // 有事件處理
                if (count > 0) {
                    // 遍歷得到 selectionKey 集合
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()) {
                        // 取出 selectionKey
                        SelectionKey key = iterator.next();
                        // 監(jiān)聽到 accept
                        if (key.isAcceptable()) {
                            SocketChannel sc = listenChannel.accept();
                            sc.configureBlocking(false);
                            // 將該 sc 注冊到 selector
                            sc.register(selector, SelectionKey.OP_READ);
                            // 提示
                            System.out.println(sc.getRemoteAddress() + " 上線 ");
                        }
                        if (key.isReadable()) {// 通道發(fā)送read事件,即通道是可讀的狀態(tài)
                            // 處理讀(專門寫方法..)
                            readData(key);
                        }
                        // 當前的 key 刪除,防止重復(fù)處理
                        iterator.remove();
                    }
                } else {
                    System.out.println("等待....");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 發(fā)生異常處理....
        }
    }

    // 讀取客戶端消息
    public void readData(SelectionKey key) {
        SocketChannel channel = null;
        try {
            // 得到 channel
            channel = (SocketChannel) key.channel();
            // 創(chuàng)建 buffer
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int count = channel.read(buffer);// NIO這里不會阻塞 因為事件觸發(fā)時必然已經(jīng)有數(shù)據(jù)了 所以叫非阻塞IO
            // 根據(jù) count 的值做處理
            if (count > 0) {
                // 把緩存區(qū)的數(shù)據(jù)轉(zhuǎn)成字符串
                String msg = new String(buffer.array());
                // 輸出該消息
                System.out.println("form客戶端:" + msg);
                // 向其它的客戶端轉(zhuǎn)發(fā)消息(去掉自己),專門寫一個方法來處理
                sendInfoToOtherClients(msg, channel);
            }
        } catch (IOException e) {
            try {
                System.out.println(channel.getRemoteAddress() + "離線了..");
                // 取消注冊
                key.cancel();
                // 關(guān)閉通道
                channel.close();
            } catch (IOException e2) {
                e2.printStackTrace();
            }
        }
    }

    // 轉(zhuǎn)發(fā)消息給其它客戶(通道)
    private void sendInfoToOtherClients(String msg, SocketChannel self) throws IOException {
        System.out.println("服務(wù)器轉(zhuǎn)發(fā)消息中...");
        // 遍歷所有注冊到 selector 上的 SocketChannel,并排除 self
        for (SelectionKey key : selector.keys()) {
            // 通過 key 取出對應(yīng)的 SocketChannel
            Channel targetChannel = key.channel();
            // 排除自己
            if (targetChannel instanceof SocketChannel && targetChannel != self) {
                // 轉(zhuǎn)型
                SocketChannel dest = (SocketChannel) targetChannel;
                // 將 msg 存儲到 buffer
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                // 將 buffer 的數(shù)據(jù)寫入通道
                dest.write(buffer);
            }
        }
    }

    public static void main(String[] args) {
        // 創(chuàng)建服務(wù)器對象
        GroupChatServer groupChatServer = new GroupChatServer();
        groupChatServer.listen();
    }
}

Java AIO

AIO 是異步非阻塞的,引入了異步通道的概念,采用 Proactor 模式,操作系統(tǒng)完成數(shù)據(jù)拷貝操作后才會通知服務(wù)端線程。AIO 本質(zhì)上還是 IO 多路復(fù)用模型,與 NIO 比起來,AIO 只是在非阻塞的前提下增加了異步功能,具體則體現(xiàn)在代碼編寫以及數(shù)據(jù)傳輸兩個層面。

  • 從代碼編寫角度來說,原來的同步方法會阻塞等待接口返回,而現(xiàn)在可以異步等待返回結(jié)果。
  • 從數(shù)據(jù)傳輸角度來說,每個請求都需要傳輸數(shù)據(jù),NIO 雖然是非阻塞的,但是事件到達后,NIO 需要自己把數(shù)據(jù)從內(nèi)核空間復(fù)制到用戶空間。AIO 引入異步邏輯后,事件到達后系統(tǒng)不會立刻通知服務(wù)端線程,而是會自己把數(shù)據(jù)從內(nèi)核空間復(fù)制到用戶空間,完成這個操作后,才會通知服務(wù)端線程去處理。

AIO 的使用場景還是比較少,現(xiàn)在大部分開源框架中應(yīng)該還是以使用 NIO 為主,AIO 在性能方面的提升還是比較有限,主要的變化還是增加了異步功能。

如何理解 Reactor 和 Proactor 的區(qū)別?

Reactor 可以理解為「來了事件操作系統(tǒng)通知應(yīng)用進程,讓應(yīng)用進程來處理」,而Proactor 可以理解為「來了事件操作系統(tǒng)來處理,處理完再通知應(yīng)用進程」。

總結(jié)

到此這篇關(guān)于Java網(wǎng)絡(luò)IO模型(BIO、NIO、AIO)的文章就介紹到這了,更多相關(guān)Java網(wǎng)絡(luò)BIO、NIO、AIO內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Java中布隆過濾器(Bloom Filter)原理及其使用場景

    詳解Java中布隆過濾器(Bloom Filter)原理及其使用場景

    布隆過濾器是1970年由布隆提出的,它實際上是一個很長的二進制向量和一系列隨機映射函數(shù),它的作用是檢索一個元素是否存在我們的集合之中,本文給大家詳細的講解一下布隆過濾器,感興趣的同學可以參考閱讀
    2023-05-05
  • spring中實現(xiàn)容器加載完成后再執(zhí)行自己的方法

    spring中實現(xiàn)容器加載完成后再執(zhí)行自己的方法

    這篇文章主要介紹了spring中實現(xiàn)容器加載完成后再執(zhí)行自己的方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • 如何實現(xiàn)自己的spring boot starter

    如何實現(xiàn)自己的spring boot starter

    這篇文章主要介紹了如何實現(xiàn)自己的spring boot starter,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-08-08
  • 詳談java命令的本質(zhì)邏輯揭秘

    詳談java命令的本質(zhì)邏輯揭秘

    一個簡單的java命令背后究竟做了些什么事情,很多朋友提出幾個問題,下面帶領(lǐng)大家一起學習Java命令的本質(zhì)邏輯問題,感興趣的朋友跟隨小編一起看看吧
    2021-05-05
  • Spring框架學習之Spring?@Autowired實現(xiàn)自動裝配的代碼

    Spring框架學習之Spring?@Autowired實現(xiàn)自動裝配的代碼

    自動裝配就是說,你不用手動實現(xiàn)bean之間的組合關(guān)系,只要使用了@Autowired注解,程序就會自動的注入這個需要的bean,前提是你的Spring容器有這個bean,這篇文章主要介紹了Spring?@Autowired實現(xiàn)自動裝配,需要的朋友可以參考下
    2021-12-12
  • Java單例模式利用HashMap實現(xiàn)緩存數(shù)據(jù)

    Java單例模式利用HashMap實現(xiàn)緩存數(shù)據(jù)

    這篇文章主要為大家詳細介紹了Java單例模式利用HashMap實現(xiàn)緩存數(shù)據(jù),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Java實現(xiàn)指定目錄下的文件查找詳解

    Java實現(xiàn)指定目錄下的文件查找詳解

    這篇文章主要介紹了利用Java實現(xiàn)指定目錄下的文件查找的方法,能做到給出文件名,查找目錄及其子目錄中是否存在或者給出后綴名,查找目錄及其子目錄中相關(guān)的文件,感興趣的可以學習一下
    2022-03-03
  • idea導入項目不顯示maven側(cè)邊欄的問題及解決方法

    idea導入項目不顯示maven側(cè)邊欄的問題及解決方法

    這篇文章主要介紹了idea導入項目不顯示maven側(cè)邊欄的問題及解決方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07
  • java通過jni調(diào)用opencv處理圖像的方法

    java通過jni調(diào)用opencv處理圖像的方法

    今天小編就為大家分享一篇java通過jni調(diào)用opencv處理圖像的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • 簡述Java圖像傾斜方法及實例

    簡述Java圖像傾斜方法及實例

    這篇文章主要介紹了Java圖像傾斜的方法及實例,需要的朋友可以參考下
    2017-09-09

最新評論

天津市| 平湖市| 民县| 大宁县| 濉溪县| 侯马市| 沧州市| 象州县| 永泰县| 兰坪| 新建县| 交城县| 宜兰市| 柘荣县| 兰坪| 潼南县| 乌兰浩特市| 舞钢市| 陈巴尔虎旗| 焦作市| 米脂县| 伊春市| 东兴市| 新巴尔虎右旗| 六安市| 祁东县| 会泽县| 咸丰县| 固原市| 华池县| 孟连| 嘉义县| 卢湾区| 布尔津县| 闻喜县| 石门县| 景宁| 新河县| 汕尾市| 二连浩特市| 建德市|