激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

香港云服务器
服務器之家 - 編程語言 - Java教程 - Java 中的io模型詳解

Java 中的io模型詳解

2021-08-31 11:59Bridge Li Java教程

這篇文章主要介紹了Java 中io模型的相關資料,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下

1. BIO

我們先看一個 Java 例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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 模型,該模型有兩個大問題:1. accept 是阻塞的;2. read 也是阻塞的,也就是說我們的服務器起來之后,首先會在 accept 處阻塞,等待客戶端連接,但有一個客戶端連接的時候,我們可以從客戶端處讀取數據,這個時候也是阻塞的,所以我們的系統只能是單連接的,當有多個客戶端連接的時候,只能一個一個的排著隊連接,然后從客戶端中讀取數據,為了實現多連接,這就要求我們必須啟用線程來解決,最開始等待客戶端連接,然后有一個客戶端連上了之后,啟動一個線程讀取客戶端的數據,然后主線程繼續等待客戶端連接。

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

為了解決此問題,有人對服務器的線程模型進行優化,服務端采用線程池來處理多個客戶端請求。但是同樣是有問題的,

1. 線程總數有限,又要等待;

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

2. NIO

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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();
                }
            }
        }
    }
 
}

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

3. 多路復用

為了解決 NIO 中無效讀取的問題,這個時候我們可以根據事件監聽,告訴操作系統說,我們監聽那些事件,然后當這些事件有數據到達時通知我們去讀取,例子如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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("服務器啟動了...");
        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();
    }
}

再多路復用中,有 poll、epoll、Selector 等實現方式,其中他們的區別是,poll 需要我們每次告訴操作系統說,我們都要關注哪些事件,而 epoll 是操作系統會開辟一塊內存區域,存儲下我們要關注的事件,不用每次都告訴操作系統我們關注哪些事件。

關于 BIO、NIO、多路復用,馬士兵教育的周志磊老師有一個很形象的例子。BIO 是阻塞的,所以需要我們每連接每線程,就相當于我們為每一輛車在收費站修建一條路,每來一輛車就要修一條路,我們我們自己從車上卸下裝的貨;NIO 是非阻塞的,我們就需要我們每次都跑到收費站,然后看我們修好的路上面車來了沒有,沒有來的話,等下次在看,來的話,我們卸下貨,再等下次看有沒有新貨;多路復用中的 poll,就是我們在收費站安裝一個電話機,然后我們每次打電話,我關注的哪些路是否有車來了,需要我卸貨,而 epoll 是我們不僅在收費站安裝了一個電話機,我們還留下了一個本子,我們每次打電話的時候,會把我們新關注的路告訴收費站,收費站在本子上記下我們關注的那些路,假設我們關注一萬條路,這樣就不需要我們每次在電話中每次把這一萬條路說一邊,問這些路是否有車來了,需要我們卸貨。

最后再說幾個小問題

1. 我們學習 IO 模型,IO 模型是操作系統提供給我們的接口,屬于系統調用,所以我們可以通過 strace 追蹤到每一個程序所執行的系統調用。命令如下:

?
1
strace -ff -o out + 要追蹤的進程

2. 當我們追蹤 BIO 的時候,因為 JDK 的優化,所以如果使用高版本的 JDK,也不會看到阻塞,這個時候你可以通過 JDK1.4 編譯運行(這也是為什么我們使用 lambda 表達式和 try-with-resource 的原因)

3. IO 調用屬于系統調用,所以從 BIO -> NIO -> 多路復用,是操作系統的進步,而我們各種變成語言寫的屬于應用,所以有沒有 異步非阻塞IO 模型,這樣看操作系統底層有沒有這樣的模型,需要操作系統給我們提供 異步非阻塞IO 相關的接口,我們的應用才能進一步優化

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

以上就是Java 中的io模型詳解的詳細內容,更多關于Java io模型的資料請關注服務器之家其它相關文章!

原文鏈接:http://www.bridgeli.cn/archives/700

延伸 · 閱讀

精彩推薦
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7472021-02-04
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
1088
主站蜘蛛池模板: 国产91免费看 | 悠悠成人资源亚洲一区二区 | 天堂在线中文资源 | 欧美视频一二三区 | 一本在线高清码电影 | 日韩视频在线不卡 | 性生活视频网站 | 欧美黄色一级生活片 | 99在线热播精品免费 | 欧美一级xxx| a视频在线看 | 欧美在线观看视频一区二区 | 超碰97人| 一区二区三区日韩在线观看 | 免费看黄色一级大片 | www.狠狠操.com | 久久国产精品网 | 在线成人免费网站 | 中午字幕无线码一区2020 | 精品国产一区二区三区在线观看 | 深夜福利视频免费观看 | 青青操精品 | 国产成人av一区二区 | 91精品免费观看 | 斗罗破苍穹在线观看免费完整观看 | 国产美女一区二区在线观看 | 亚洲日色 | 91成人一区 | 国产亚洲高清在线精品不卡 | www.成人免费视频 | 国产精品久久久久久久久久大牛 | 亚洲精品一区二区三区大胸 | 久久精品视频69 | 成人 日韩 | 久草在线综合 | 91av99| 爽毛片 | 欧美成人黄色小视频 | 色就色 综合偷拍区91网 | 黄色淫片| 精品中文字幕视频 |