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

Java 中的io模型詳解

 更新時(shí)間:2021年04月01日 09:12:31   作者:Bridge Li  
這篇文章主要介紹了Java 中io模型的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下

1. BIO

我們先看一個(gè) Java 例子:

package cn.bridgeli.demo;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
 
/**
 * @author bridgeli
 */
public class SocketBIO {
 
 
    public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(9090, 20);
 
        System.out.println("step1: new ServerSocket(9090) ");
 
        while (true) {
            Socket client = server.accept();
            System.out.println("step2:client: " + client.getPort());
 
            new Thread(new Runnable() {
                @Override
                public void run() {
                    InputStream inputStream = null;
                    BufferedReader reader = null;
                    try {
                        inputStream = client.getInputStream();
                        reader = new BufferedReader(new InputStreamReader(inputStream));
                        while (true) {
                            String dataLine = reader.readLine(); //阻塞2
                            if (null != dataLine) {
                                System.out.println(dataLine);
                            } else {
                                client.close();
                                break;
                            }
                        }
                        System.out.println("客戶端斷開");
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (null != reader) {
                            try {
                                reader.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        if (null!= inputStream) {
                            try {
                                inputStream.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
 
                }
 
 
            }).start();
 
        }
    }
 
}

BIO 是最初始的 IO 模型,該模型有兩個(gè)大問題:1. accept 是阻塞的;2. read 也是阻塞的,也就是說我們的服務(wù)器起來之后,首先會(huì)在 accept 處阻塞,等待客戶端連接,但有一個(gè)客戶端連接的時(shí)候,我們可以從客戶端處讀取數(shù)據(jù),這個(gè)時(shí)候也是阻塞的,所以我們的系統(tǒng)只能是單連接的,當(dāng)有多個(gè)客戶端連接的時(shí)候,只能一個(gè)一個(gè)的排著隊(duì)連接,然后從客戶端中讀取數(shù)據(jù),為了實(shí)現(xiàn)多連接,這就要求我們必須啟用線程來解決,最開始等待客戶端連接,然后有一個(gè)客戶端連上了之后,啟動(dòng)一個(gè)線程讀取客戶端的數(shù)據(jù),然后主線程繼續(xù)等待客戶端連接。

該模型最大的問題就是缺乏彈性伸縮能力,當(dāng)客戶端并發(fā)訪問量增加后,服務(wù)端的線程個(gè)數(shù)和客戶端并發(fā)訪問數(shù)呈1:1的正比關(guān)系,Java 中的線程也是比較寶貴的系統(tǒng)資源,線程數(shù)量快速膨脹后,系統(tǒng)的性能將急劇下降,隨著訪問量的繼續(xù)增大,系統(tǒng)最終就死掉了。當(dāng)然不僅僅是 Java,我們直接設(shè)想假設(shè)有一萬個(gè)客戶端連接到服務(wù)端,服務(wù)端要開一萬個(gè)線程,那么這個(gè)時(shí)候服務(wù)端光開線程要占用多少資源?需要多大內(nèi)存?操作系統(tǒng)為了調(diào)度這些線程 CPU 是不是也要被占用完了?

為了解決此問題,有人對(duì)服務(wù)器的線程模型進(jìn)行優(yōu)化,服務(wù)端采用線程池來處理多個(gè)客戶端請(qǐng)求。但是同樣是有問題的,

1. 線程總數(shù)有限,又要等待;

2. 多余的連接會(huì)堆積在任務(wù)隊(duì)列中,當(dāng)任務(wù)隊(duì)列滿了,那么此時(shí)就開始啟用拒絕策略了,所以還是沒有從根本上解決問題。

2. NIO

BIO 最大的問題,在于 B,block,阻塞,所以只要解決了這個(gè)問題就可以,那么此時(shí) NIO 應(yīng)運(yùn)而生,N 就是 non-block 的意思(Java 中是 new 的意思),同樣先看一個(gè)例子:

package cn.bridgeli.demo;
 
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;
 
/**
 * @author bridgeli
 */
public class SocketNIO {
 
    public static void main(String[] args) throws Exception {
 
        LinkedList<SocketChannel> clients = new LinkedList<>();
 
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(9090));
        serverSocketChannel.configureBlocking(false);
 
        while (true) {
            SocketChannel client = serverSocketChannel.accept();
 
            if (null != client) {
                client.configureBlocking(false);
                System.out.println("client port: " + client.socket().getPort());
                clients.add(client);
            }
 
            ByteBuffer buffer = ByteBuffer.allocateDirect(4096);
 
            for (SocketChannel c : clients) {
                int num = c.read(buffer);
                if (num > 0) {
                    buffer.flip();
                    byte[] aaa = new byte[buffer.limit()];
                    buffer.get(aaa);
 
                    String b = new String(aaa);
                    System.out.println(c.socket().getPort() + " : " + b);
                    buffer.clear();
                }
            }
        }
    }
 
}

這個(gè)時(shí)候我們會(huì)發(fā)現(xiàn)連接和讀取都是非阻塞的了,由于都是非阻塞的,所以這就要求我們需要有一個(gè)集合,用來存儲(chǔ)所有的連接,然后從連接中讀取數(shù)據(jù)。這個(gè)模型解決了我們需要開線程的問題,沒循環(huán)一次,如果有新連接過來,我們就把連接放到集合中,然后挨個(gè)讀取連接中的數(shù)據(jù),此時(shí)就不需要我們每連接每線程了,但是還是有一個(gè)問題,隨著連接的增加,我們的隊(duì)列會(huì)越來越大,而且我們每次都要遍歷所有的連接讀取數(shù)據(jù),我們還假設(shè)有一萬個(gè)連接,但是前 9999 個(gè)連接都沒有數(shù)據(jù),只有最后一個(gè)連接有數(shù)據(jù),那前 9999 次讀取都是浪費(fèi)。

3. 多路復(fù)用

為了解決 NIO 中無效讀取的問題,這個(gè)時(shí)候我們可以根據(jù)事件監(jiān)聽,告訴操作系統(tǒng)說,我們監(jiān)聽那些事件,然后當(dāng)這些事件有數(shù)據(jù)到達(dá)時(shí)通知我們?nèi)プx取,例子如下:

package cn.bridgeli.demo;
 
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
 
/**
 * @author bridgeli
 */
public class SocketMultiplexingIO {
 
    private ServerSocketChannel serverSocketChannel = null;
    private Selector selector = null;
 
    public void initServer() {
        try {
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.bind(new InetSocketAddress(9090));
            selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public void start() {
        initServer();
        System.out.println("服務(wù)器啟動(dòng)了...");
        try {
            while (true) {
 
                while (selector.select() > 0) {
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = selectionKeys.iterator();
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();
                        iterator.remove();
                        if (key.isAcceptable()) {
                            acceptHandler(key);
                        } else if (key.isReadable()) {
                            readHandler(key);
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public void acceptHandler(SelectionKey key) {
        try {
            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
            SocketChannel client = ssc.accept();
            client.configureBlocking(false);
 
            ByteBuffer buffer = ByteBuffer.allocate(8192);
 
            client.register(selector, SelectionKey.OP_READ, buffer);
            System.out.println("新客戶端:" + client.getRemoteAddress());
 
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public void readHandler(SelectionKey key) {
        SocketChannel client = (SocketChannel) key.channel();
        ByteBuffer buffer = (ByteBuffer) key.attachment();
        buffer.clear();
        int read = 0;
        try {
            while (true) {
                read = client.read(buffer);
                if (read > 0) {
                    buffer.flip();
                    while (buffer.hasRemaining()) {
                        client.write(buffer);
                    }
                    buffer.clear();
                } else if (read == 0) {
                    break;
                } else {
                    client.close();
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
 
        }
    }
 
    public static void main(String[] args) {
        SocketMultiplexingIO service = new SocketMultiplexingIO();
        service.start();
    }
}

再多路復(fù)用中,有 poll、epoll、Selector 等實(shí)現(xiàn)方式,其中他們的區(qū)別是,poll 需要我們每次告訴操作系統(tǒng)說,我們都要關(guān)注哪些事件,而 epoll 是操作系統(tǒng)會(huì)開辟一塊內(nèi)存區(qū)域,存儲(chǔ)下我們要關(guān)注的事件,不用每次都告訴操作系統(tǒng)我們關(guān)注哪些事件。

關(guān)于 BIO、NIO、多路復(fù)用,馬士兵教育的周志磊老師有一個(gè)很形象的例子。BIO 是阻塞的,所以需要我們每連接每線程,就相當(dāng)于我們?yōu)槊恳惠v車在收費(fèi)站修建一條路,每來一輛車就要修一條路,我們我們自己從車上卸下裝的貨;NIO 是非阻塞的,我們就需要我們每次都跑到收費(fèi)站,然后看我們修好的路上面車來了沒有,沒有來的話,等下次在看,來的話,我們卸下貨,再等下次看有沒有新貨;多路復(fù)用中的 poll,就是我們?cè)谑召M(fèi)站安裝一個(gè)電話機(jī),然后我們每次打電話,我關(guān)注的哪些路是否有車來了,需要我卸貨,而 epoll 是我們不僅在收費(fèi)站安裝了一個(gè)電話機(jī),我們還留下了一個(gè)本子,我們每次打電話的時(shí)候,會(huì)把我們新關(guān)注的路告訴收費(fèi)站,收費(fèi)站在本子上記下我們關(guān)注的那些路,假設(shè)我們關(guān)注一萬條路,這樣就不需要我們每次在電話中每次把這一萬條路說一邊,問這些路是否有車來了,需要我們卸貨。

最后再說幾個(gè)小問題

1. 我們學(xué)習(xí) IO 模型,IO 模型是操作系統(tǒng)提供給我們的接口,屬于系統(tǒng)調(diào)用,所以我們可以通過 strace 追蹤到每一個(gè)程序所執(zhí)行的系統(tǒng)調(diào)用。命令如下:

strace -ff -o out + 要追蹤的進(jìn)程

2. 當(dāng)我們追蹤 BIO 的時(shí)候,因?yàn)?JDK 的優(yōu)化,所以如果使用高版本的 JDK,也不會(huì)看到阻塞,這個(gè)時(shí)候你可以通過 JDK1.4 編譯運(yùn)行(這也是為什么我們使用 lambda 表達(dá)式和 try-with-resource 的原因)

3. IO 調(diào)用屬于系統(tǒng)調(diào)用,所以從 BIO -> NIO -> 多路復(fù)用,是操作系統(tǒng)的進(jìn)步,而我們各種變成語言寫的屬于應(yīng)用,所以有沒有 異步非阻塞IO 模型,這樣看操作系統(tǒng)底層有沒有這樣的模型,需要操作系統(tǒng)給我們提供 異步非阻塞IO 相關(guān)的接口,我們的應(yīng)用才能進(jìn)一步優(yōu)化

4. 我們通過 strace 追蹤到的每一個(gè)系統(tǒng)調(diào)用,都可以通過 man 命令查看文檔(僅限 linux 系統(tǒng),非 Windows 系統(tǒng)),如果沒有 man 命令,安裝一下就可以了。

以上就是Java 中的io模型詳解的詳細(xì)內(nèi)容,更多關(guān)于Java io模型的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 分享令人目瞪口呆的?Java?代碼技巧

    分享令人目瞪口呆的?Java?代碼技巧

    這篇文章主要介紹了令人目瞪口呆的?Java?代碼技巧,本文從寫?Java?程序的小方面一直寫到大方面,來闡述了如何才能寫好?Java?程序,并告訴讀者們?nèi)绾尾拍芴岣咦陨淼木幋a水平,需要的朋友可以參考下
    2022-05-05
  • Spring?Boot指定外部配置文件簡(jiǎn)單示例

    Spring?Boot指定外部配置文件簡(jiǎn)單示例

    Spring Boot可以讓你將配置外部化,這樣你就可以在不同的環(huán)境中使用相同的應(yīng)用程序代碼,這篇文章主要給大家介紹了關(guān)于Spring?Boot指定外部配置文件的相關(guān)資料,需要的朋友可以參考下
    2024-01-01
  • 深入理解Spring Cloud Zuul過濾器

    深入理解Spring Cloud Zuul過濾器

    這篇文章主要給大家介紹了關(guān)于Spring Cloud Zuul過濾器的相關(guān)資料,通過閱讀本文您將了解:Zuul過濾器類型與請(qǐng)求生命周期、如何編寫Zuul過濾器、如何禁用Zuul過濾器和Spring Cloud為Zuul編寫的過濾器及其功能,需要的朋友可以參考下。
    2017-02-02
  • Java數(shù)據(jù)結(jié)構(gòu)之ArrayList從順序表到實(shí)現(xiàn)

    Java數(shù)據(jù)結(jié)構(gòu)之ArrayList從順序表到實(shí)現(xiàn)

    Java中的ArrayList是一種基于數(shù)組實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu),支持動(dòng)態(tài)擴(kuò)容和隨機(jī)訪問元素,可用于實(shí)現(xiàn)順序表等數(shù)據(jù)結(jié)構(gòu)。ArrayList在內(nèi)存中連續(xù)存儲(chǔ)元素,支持快速的隨機(jī)訪問和遍歷。通過學(xué)習(xí)ArrayList的實(shí)現(xiàn)原理和使用方法,可以更好地掌握J(rèn)ava中的數(shù)據(jù)結(jié)構(gòu)和算法
    2023-04-04
  • Spring JDBC參數(shù)處理與嵌入式數(shù)據(jù)庫(kù)的實(shí)戰(zhàn)指南

    Spring JDBC參數(shù)處理與嵌入式數(shù)據(jù)庫(kù)的實(shí)戰(zhàn)指南

    這篇文章介紹了Spring Framework中JDBC的支持,包括如何處理參數(shù)和數(shù)據(jù)值、處理BLOB和CLOB、為IN子句傳遞列表以及存儲(chǔ)過程調(diào)用中的復(fù)雜類型,本文用通俗易懂的方式講解Spring JDBC參數(shù)處理與嵌入式數(shù)據(jù)庫(kù)的相關(guān)操作,感興趣的朋友跟隨小編一起看看吧
    2025-10-10
  • SpringBoot3.0整合chatGPT的完整步驟

    SpringBoot3.0整合chatGPT的完整步驟

    ChatGPT是OpenAI推出的一個(gè)語言模型系統(tǒng),它能夠?qū)崟r(shí)回答用戶提問,包括聊天、糾正語法錯(cuò)誤,甚至是寫代碼、寫劇本等,由于可玩性很高,迅速在全球范圍內(nèi)風(fēng)靡起來,下面這篇文章主要給大家介紹了關(guān)于SpringBoot3.0整合chatGPT的完整步驟,需要的朋友可以參考下
    2022-12-12
  • Java?@SpringBootApplication注解深入解析

    Java?@SpringBootApplication注解深入解析

    這篇文章主要給大家介紹了關(guān)于Java?@SpringBootApplication注解的相關(guān)資料,@SpringBootApplication這個(gè)注解是Spring?Boot項(xiàng)目的基石,創(chuàng)建SpringBoot項(xiàng)目之后會(huì)默認(rèn)在主類加上,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-02-02
  • Java中泛型的用法總結(jié)

    Java中泛型的用法總結(jié)

    這篇文章主要介紹了Java中泛型的用法,實(shí)例總結(jié)了java中泛型的常見使用技巧,需要的朋友可以參考下
    2015-06-06
  • springbean的加載過程及應(yīng)用場(chǎng)景分析

    springbean的加載過程及應(yīng)用場(chǎng)景分析

    Bean已經(jīng)被實(shí)例化、屬性注入、初始化,并且注冊(cè)到容器中,可以被其他Bean或應(yīng)用程序使用,這篇文章主要介紹了springbean的加載過程以及應(yīng)用場(chǎng)景,需要的朋友可以參考下
    2024-04-04
  • 關(guān)于如何搭建CAS服務(wù)并將CAS項(xiàng)目導(dǎo)入IDEA

    關(guān)于如何搭建CAS服務(wù)并將CAS項(xiàng)目導(dǎo)入IDEA

    這篇文章主要介紹了關(guān)于如何搭建CAS服務(wù)并將CAS項(xiàng)目導(dǎo)入IDEA的問題,文中提供了詳細(xì)的圖文講解,需要的朋友可以參考下,如果有錯(cuò)誤的地方還請(qǐng)指正
    2023-03-03

最新評(píng)論

兴隆县| 济阳县| 崇礼县| 买车| 旌德县| 噶尔县| 车险| 芜湖市| 仁化县| 年辖:市辖区| 贡嘎县| 张家口市| 阿图什市| 岳阳县| 囊谦县| 兰州市| 玉树县| 大宁县| 赣州市| 卢氏县| 咸宁市| 冷水江市| 万源市| 合山市| 永顺县| 杨浦区| 瑞安市| 翁牛特旗| 伊金霍洛旗| 沁水县| 焉耆| 龙南县| 卢氏县| 崇礼县| 南漳县| 枞阳县| 阳城县| 九龙县| 鸡东县| 广宁县| 塘沽区|