真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java非阻塞IO和異步IO的詳細介紹

這篇文章主要講解了“Java非阻塞IO和異步IO的詳細介紹”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Java非阻塞IO和異步IO的詳細介紹”吧!

網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)建站!專注于網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、成都小程序開發(fā)、集團企業(yè)網(wǎng)站建設(shè)等服務(wù)項目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了東源免費建站歡迎大家使用!

阻塞模式 IO

我們已經(jīng)介紹過使用 Java NIO 包組成一個簡單的客戶端-服務(wù)端網(wǎng)絡(luò)通訊所需要的 ServerSocketChannel、SocketChannel 和 Buffer,我們這里整合一下它們,給出一個完整的可運行的例子:

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 監(jiān)聽 8080 端口進來的 TCP 鏈接
        serverSocketChannel.socket().bind(new InetSocketAddress(8080));
        while (true) {
            // 這里會阻塞,直到有一個請求的連接進來
            SocketChannel socketChannel = serverSocketChannel.accept();
            // 開啟一個新的線程來處理這個請求,然后在 while 循環(huán)中繼續(xù)監(jiān)聽 8080 端口
            SocketHandler handler = new SocketHandler(socketChannel);
            new Thread(handler).start();
        }
    }
}

這里看一下新的線程需要做什么,SocketHandler:

public class SocketHandler implements Runnable {
    private SocketChannel socketChannel;
    public SocketHandler(SocketChannel socketChannel) {
        this.socketChannel = socketChannel;
    }
    @Override
    public void run() {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        try {
            // 將請求數(shù)據(jù)讀入 Buffer 中
            int num;
            while ((num = socketChannel.read(buffer)) > 0) {
                // 讀取 Buffer 內(nèi)容之前先 flip 一下
                buffer.flip();
                // 提取 Buffer 中的數(shù)據(jù)
                byte[] bytes = new byte[num];
                buffer.get(bytes);
                String re = new String(bytes, "UTF-8");
                System.out.println("收到請求:" + re);
                // 回應(yīng)客戶端
                ByteBuffer writeBuffer = ByteBuffer.wrap(("我已經(jīng)收到你的請求,你的請求內(nèi)容是:" + re).getBytes());
                socketChannel.write(writeBuffer);
                buffer.clear();
            }
        } catch (IOException e) {
            IOUtils.closeQuietly(socketChannel);
        }
    }
}

最后,貼一下客戶端 SocketChannel 的使用,客戶端比較簡單:

public class SocketChannelTest {
    public static void main(String[] args) throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("localhost", 8080));
        // 發(fā)送請求
        ByteBuffer buffer = ByteBuffer.wrap("1234567890".getBytes());
        socketChannel.write(buffer);
        // 讀取響應(yīng)
        ByteBuffer readBuffer = ByteBuffer.allocate(1024);
        int num;
        if ((num = socketChannel.read(readBuffer)) > 0) {
            readBuffer.flip();
            byte[] re = new byte[num];
            readBuffer.get(re);
            String result = new String(re, "UTF-8");
            System.out.println("返回值: " + result);
        }
    }
}

上面介紹的阻塞模式的代碼應(yīng)該很好理解:來一個新的連接,我們就新開一個線程來處理這個連接,之后的操作全部由那個線程來完成。

那么,這個模式下的性能瓶頸在哪里呢?

  1. 首先,每次來一個連接都開一個新的線程這肯定是不合適的。當(dāng)活躍連接數(shù)在幾十幾百的時候當(dāng)然是可以這樣做的,但如果活躍連接數(shù)是幾萬幾十萬的時候,這么多線程明顯就不行了。每個線程都需要一部分內(nèi)存,內(nèi)存會被迅速消耗,同時,線程切換的開銷非常大。

  2. 其次,阻塞操作在這里也是一個問題。首先,accept() 是一個阻塞操作,當(dāng) accept() 返回的時候,代表有一個連接可以使用了,我們這里是馬上就新建線程來處理這個 SocketChannel 了,但是,但是這里不代表對方就將數(shù)據(jù)傳輸過來了。所以,SocketChannel#read 方法將阻塞,等待數(shù)據(jù),明顯這個等待是不值得的。同理,write 方法也需要等待通道可寫才能執(zhí)行寫入操作,這邊的阻塞等待也是不值得的。

非阻塞 IO

說完了阻塞模式的使用及其缺點以后,我們這里就可以介紹非阻塞 IO 了。

非阻塞 IO 的核心在于使用一個 Selector 來管理多個通道,可以是 SocketChannel,也可以是 ServerSocketChannel,將各個通道注冊到 Selector 上,指定監(jiān)聽的事件。

之后可以只用一個線程來輪詢這個 Selector,看看上面是否有通道是準備好的,當(dāng)通道準備好可讀或可寫,然后才去開始真正的讀寫,這樣速度就很快了。我們就完全沒有必要給每個通道都起一個線程。

NIO 中 Selector 是對底層操作系統(tǒng)實現(xiàn)的一個抽象,管理通道狀態(tài)其實都是底層系統(tǒng)實現(xiàn)的,這里簡單介紹下在不同系統(tǒng)下的實現(xiàn)。

select:上世紀 80 年代就實現(xiàn)了,它支持注冊 FD_SETSIZE(1024) 個 socket,在那個年代肯定是夠用的,不過現(xiàn)在嘛,肯定是不行了。

poll:1997 年,出現(xiàn)了 poll 作為 select 的替代者,最大的區(qū)別就是,poll 不再限制 socket 數(shù)量。

select 和 poll 都有一個共同的問題,那就是它們都只會告訴你有幾個通道準備好了,但是不會告訴你具體是哪幾個通道。所以,一旦知道有通道準備好以后,自己還是需要進行一次掃描,顯然這個不太好,通道少的時候還行,一旦通道的數(shù)量是幾十萬個以上的時候,掃描一次的時間都很可觀了,時間復(fù)雜度 O(n)。所以,后來才催生了以下實現(xiàn)。

epoll:2002 年隨 Linux 內(nèi)核 2.5.44 發(fā)布,epoll 能直接返回具體的準備好的通道,時間復(fù)雜度 O(1)。

除了 Linux 中的 epoll,2000 年 FreeBSD 出現(xiàn)了 Kqueue,還有就是,Solaris 中有 /dev/poll。

前面說了那么多實現(xiàn),但是沒有出現(xiàn) Windows,Windows 平臺的非阻塞 IO 使用 select,我們也不必覺得 Windows 很落后,在 Windows 中 IOCP 提供的異步 IO 是比較強大的。

我們回到 Selector,畢竟 JVM 就是這么一個屏蔽底層實現(xiàn)的平臺,我們面向 Selector 編程就可以了。

之前在介紹 Selector 的時候已經(jīng)了解過了它的基本用法,這邊來一個可運行的實例代碼,大家不妨看看:

public class SelectorServer {
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel server = ServerSocketChannel.open();
        server.socket().bind(new InetSocketAddress(8080));
        // 將其注冊到 Selector 中,監(jiān)聽 OP_ACCEPT 事件
        server.configureBlocking(false);
        server.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            int readyChannels = selector.select();
            if (readyChannels == 0) {
                continue;
            }
            Set readyKeys = selector.selectedKeys();
            // 遍歷
            Iterator iterator = readyKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                if (key.isAcceptable()) {
                    // 有已經(jīng)接受的新的到服務(wù)端的連接
                    SocketChannel socketChannel = server.accept();
                    // 有新的連接并不代表這個通道就有數(shù)據(jù),
                    // 這里將這個新的 SocketChannel 注冊到 Selector,監(jiān)聽 OP_READ 事件,等待數(shù)據(jù)
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    // 有數(shù)據(jù)可讀
                    // 上面一個 if 分支中注冊了監(jiān)聽 OP_READ 事件的 SocketChannel
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                    int num = socketChannel.read(readBuffer);
                    if (num > 0) {
                        // 處理進來的數(shù)據(jù)...
                        System.out.println("收到數(shù)據(jù):" + new String(readBuffer.array()).trim());
                        ByteBuffer buffer = ByteBuffer.wrap("返回給客戶端的數(shù)據(jù)...".getBytes());
                        socketChannel.write(buffer);
                    } else if (num == -1) {
                        // -1 代表連接已經(jīng)關(guān)閉
                        socketChannel.close();
                    }
                }
            }
        }
    }
}

至于客戶端,大家可以繼續(xù)使用上一節(jié)介紹阻塞模式時的客戶端進行測試。

NIO.2 異步 IO

More New IO,或稱 NIO.2,隨 JDK 1.7 發(fā)布,包括了引入異步 IO 接口和 Paths 等文件訪問接口。

異步這個詞,我想對于絕大多數(shù)開發(fā)者來說都很熟悉,很多場景下我們都會使用異步。

通常,我們會有一個線程池用于執(zhí)行異步任務(wù),提交任務(wù)的線程將任務(wù)提交到線程池就可以立馬返回,不必等到任務(wù)真正完成。如果想要知道任務(wù)的執(zhí)行結(jié)果,通常是通過傳遞一個回調(diào)函數(shù)的方式,任務(wù)結(jié)束后去調(diào)用這個函數(shù)。

同樣的原理,Java 中的異步 IO 也是一樣的,都是由一個線程池來負責(zé)執(zhí)行任務(wù),然后使用回調(diào)或自己去查詢結(jié)果。

大部分開發(fā)者都知道為什么要這么設(shè)計了,這里再啰嗦一下。異步 IO 主要是為了控制線程數(shù)量,減少過多的線程帶來的內(nèi)存消耗和 CPU 在線程調(diào)度上的開銷。

在 Unix/Linux 等系統(tǒng)中,JDK 使用了并發(fā)包中的線程池來管理任務(wù),具體可以查看 AsynchronousChannelGroup 的源碼。

在 Windows 操作系統(tǒng)中,提供了一個叫做 I/O Completion Ports 的方案,通常簡稱為 IOCP,操作系統(tǒng)負責(zé)管理線程池,其性能非常優(yōu)異,所以在 Windows 中 JDK 直接采用了 IOCP 的支持,使用系統(tǒng)支持,把更多的操作信息暴露給操作系統(tǒng),也使得操作系統(tǒng)能夠?qū)ξ覀兊?IO 進行一定程度的優(yōu)化。

在 Linux 中其實也是有異步 IO 系統(tǒng)實現(xiàn)的,但是限制比較多,性能也一般,所以 JDK 采用了自建線程池的方式。

本文還是以實用為主,想要了解更多信息請自行查找其他資料,下面對 Java 異步 IO 進行實踐性的介紹。

總共有三個類需要我們關(guān)注,分別是 AsynchronousSocketChannelAsynchronousServerSocketChannelAsynchronousFileChannel,只不過是在之前介紹的 FileChannel、SocketChannel 和 ServerSocketChannel 的類名上加了個前綴 Asynchronous

Java 異步 IO 提供了兩種使用方式,分別是返回 Future 實例和使用回調(diào)函數(shù)。

1、返回 Future 實例

返回 java.util.concurrent.Future 實例的方式我們應(yīng)該很熟悉,JDK 線程池就是這么使用的。Future 接口的幾個方法語義在這里也是通用的,這里先做簡單介紹。

  • future.isDone();

    判斷操作是否已經(jīng)完成,包括了正常完成、異常拋出、取消

  • future.cancel(true);

    取消操作,方式是中斷。參數(shù) true 說的是,即使這個任務(wù)正在執(zhí)行,也會進行中斷。

  • future.isCancelled();

    是否被取消,只有在任務(wù)正常結(jié)束之前被取消,這個方法才會返回 true

  • future.get();

    這是我們的老朋友,獲取執(zhí)行結(jié)果,阻塞。

  • future.get(10, TimeUnit.SECONDS);

    如果上面的 get() 方法的阻塞你不滿意,那就設(shè)置個超時時間。

2、提供 CompletionHandler 回調(diào)函數(shù)

java.nio.channels.CompletionHandler 接口定義:

public interface CompletionHandler {
    void completed(V result, A attachment);
    void failed(Throwable exc, A attachment);
}

注意,參數(shù)上有個 attachment,雖然不常用,我們可以在各個支持的方法中傳遞這個參數(shù)值

AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open().bind(null);
// accept 方法的第一個參數(shù)可以傳遞 attachment
listener.accept(attachment, new CompletionHandler() {
    public void completed(
      AsynchronousSocketChannel client, Object attachment) {
          // 
      }
    public void failed(Throwable exc, Object attachment) {
          // 
      }
});

AsynchronousFileChannel

網(wǎng)上關(guān)于 Non-Blocking IO 的介紹文章很多,但是 Asynchronous IO 的文章相對就少得多了,所以我這邊會多介紹一些相關(guān)內(nèi)容。

首先,我們就來關(guān)注異步的文件 IO,前面我們說了,文件 IO 在所有的操作系統(tǒng)中都不支持非阻塞模式,但是我們可以對文件 IO 采用異步的方式來提高性能。

下面,我會介紹 AsynchronousFileChannel 里面的一些重要的接口,都很簡單,讀者要是覺得無趣,直接滑到下一個標(biāo)題就可以了。

實例化:

AsynchronousFileChannel channel = AsynchronousFileChannel.open(Paths.get("/Users/hongjie/test.txt"));

一旦實例化完成,我們就可以著手準備將數(shù)據(jù)讀入到 Buffer 中:

ByteBuffer buffer = ByteBuffer.allocate(1024);
Future result = channel.read(buffer, 0);

異步文件通道的讀操作和寫操作都需要提供一個文件的開始位置,文件開始位置為 0

除了使用返回 Future 實例的方式,也可以采用回調(diào)函數(shù)進行操作,接口如下:

public abstract  void read(ByteBuffer dst,
                              long position,
                              A attachment,
                              CompletionHandler handler);

順便也貼一下寫操作的兩個版本的接口:

public abstract Future write(ByteBuffer src, long position);
public abstract  void write(ByteBuffer src,
                               long position,
                               A attachment,
                               CompletionHandler handler);

我們可以看到,AIO 的讀寫主要也還是與 Buffer 打交道,這個與 NIO 是一脈相承的。

另外,還提供了用于將內(nèi)存中的數(shù)據(jù)刷入到磁盤的方法:

public abstract void force(boolean metaData) throws IOException;

因為我們對文件的寫操作,操作系統(tǒng)并不會直接針對文件操作,系統(tǒng)會緩存,然后周期性地刷入到磁盤。如果希望將數(shù)據(jù)及時寫入到磁盤中,以免斷電引發(fā)部分數(shù)據(jù)丟失,可以調(diào)用此方法。參數(shù)如果設(shè)置為 true,意味著同時也將文件屬性信息更新到磁盤。

還有,還提供了對文件的鎖定功能,我們可以鎖定文件的部分數(shù)據(jù),這樣可以進行排他性的操作。

public abstract Future lock(long position, long size, boolean shared);

position 是要鎖定內(nèi)容的開始位置,size 指示了要鎖定的區(qū)域大小,shared 指示需要的是共享鎖還是排他鎖

當(dāng)然,也可以使用回調(diào)函數(shù)的版本:

public abstract  void lock(long position,
                              long size,
                              boolean shared,
                              A attachment,
                              CompletionHandler handler);

文件鎖定功能上還提供了 tryLock 方法,此方法會快速返回結(jié)果:

public abstract FileLock tryLock(long position, long size, boolean shared)
    throws IOException;

這個方法很簡單,就是嘗試去獲取鎖,如果該區(qū)域已被其他線程或其他應(yīng)用鎖住,那么立刻返回 null,否則返回 FileLock 對象。

AsynchronousFileChannel 操作大體上也就以上介紹的這些接口,還是比較簡單的,這里就少一些廢話早點結(jié)束好了。

AsynchronousServerSocketChannel

這個類對應(yīng)的是非阻塞 IO 的 ServerSocketChannel,大家可以類比下使用方式。

我們就廢話少說,用代碼說事吧:

package com.javadoop.aio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class Server {
    public static void main(String[] args) throws IOException {
          // 實例化,并監(jiān)聽端口
        AsynchronousServerSocketChannel server =
                AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8080));
        // 自己定義一個 Attachment 類,用于傳遞一些信息
        Attachment att = new Attachment();
        att.setServer(server);
        server.accept(att, new CompletionHandler() {
            @Override
            public void completed(AsynchronousSocketChannel client, Attachment att) {
                try {
                    SocketAddress clientAddr = client.getRemoteAddress();
                    System.out.println("收到新的連接:" + clientAddr);
                    // 收到新的連接后,server 應(yīng)該重新調(diào)用 accept 方法等待新的連接進來
                    att.getServer().accept(att, this);
                    Attachment newAtt = new Attachment();
                    newAtt.setServer(server);
                    newAtt.setClient(client);
                    newAtt.setReadMode(true);
                    newAtt.setBuffer(ByteBuffer.allocate(2048));
                    // 這里也可以繼續(xù)使用匿名實現(xiàn)類,不過代碼不好看,所以這里專門定義一個類
                    client.read(newAtt.getBuffer(), newAtt, new ChannelHandler());
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
            @Override
            public void failed(Throwable t, Attachment att) {
                System.out.println("accept failed");
            }
        });
        // 為了防止 main 線程退出
        try {
            Thread.currentThread().join();
        } catch (InterruptedException e) {
        }
    }
}

看一下 ChannelHandler 類:

package com.javadoop.aio;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
public class ChannelHandler implements CompletionHandler {
    @Override
    public void completed(Integer result, Attachment att) {
        if (att.isReadMode()) {
            // 讀取來自客戶端的數(shù)據(jù)
            ByteBuffer buffer = att.getBuffer();
            buffer.flip();
            byte bytes[] = new byte[buffer.limit()];
            buffer.get(bytes);
            String msg = new String(buffer.array()).toString().trim();
            System.out.println("收到來自客戶端的數(shù)據(jù): " + msg);
            // 響應(yīng)客戶端請求,返回數(shù)據(jù)
            buffer.clear();
            buffer.put("Response from server!".getBytes(Charset.forName("UTF-8")));
            att.setReadMode(false);
            buffer.flip();
            // 寫數(shù)據(jù)到客戶端也是異步
            att.getClient().write(buffer, att, this);
        } else {
            // 到這里,說明往客戶端寫數(shù)據(jù)也結(jié)束了,有以下兩種選擇:
            // 1\. 繼續(xù)等待客戶端發(fā)送新的數(shù)據(jù)過來
//            att.setReadMode(true);
//            att.getBuffer().clear();
//            att.getClient().read(att.getBuffer(), att, this);
            // 2\. 既然服務(wù)端已經(jīng)返回數(shù)據(jù)給客戶端,斷開這次的連接
            try {
                att.getClient().close();
            } catch (IOException e) {
            }
        }
    }
    @Override
    public void failed(Throwable t, Attachment att) {
        System.out.println("連接斷開");
    }
}

順便再貼一下自定義的 Attachment 類:

public class Attachment {
    private AsynchronousServerSocketChannel server;
    private AsynchronousSocketChannel client;
    private boolean isReadMode;
    private ByteBuffer buffer;
    // getter & setter
}

這樣,一個簡單的服務(wù)端就寫好了,接下來可以接收客戶端請求了。上面我們用的都是回調(diào)函數(shù)的方式,讀者要是感興趣,可以試試寫個使用 Future 的。

AsynchronousSocketChannel

其實,說完上面的 AsynchronousServerSocketChannel,基本上讀者也就知道怎么使用 AsynchronousSocketChannel 了,和非阻塞 IO 基本類似。

這邊做個簡單演示,這樣讀者就可以配合之前介紹的 Server 進行測試使用了。

package com.javadoop.aio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class Client {
    public static void main(String[] args) throws Exception {
        AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
          // 來個 Future 形式的
        Future future = client.connect(new InetSocketAddress(8080));
        // 阻塞一下,等待連接成功
        future.get();
        Attachment att = new Attachment();
        att.setClient(client);
        att.setReadMode(false);
        att.setBuffer(ByteBuffer.allocate(2048));
        byte[] data = "I am obot!".getBytes();
        att.getBuffer().put(data);
        att.getBuffer().flip();
        // 異步發(fā)送數(shù)據(jù)到服務(wù)端
        client.write(att.getBuffer(), att, new ClientChannelHandler());
        // 這里休息一下再退出,給出足夠的時間處理數(shù)據(jù)
        Thread.sleep(2000);
    }
}

往里面看下 ClientChannelHandler 類:

package com.javadoop.aio;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
public class ClientChannelHandler implements CompletionHandler {
    @Override
    public void completed(Integer result, Attachment att) {
        ByteBuffer buffer = att.getBuffer();
        if (att.isReadMode()) {
            // 讀取來自服務(wù)端的數(shù)據(jù)
            buffer.flip();
            byte[] bytes = new byte[buffer.limit()];
            buffer.get(bytes);
            String msg = new String(bytes, Charset.forName("UTF-8"));
            System.out.println("收到來自服務(wù)端的響應(yīng)數(shù)據(jù): " + msg);
            // 接下來,有以下兩種選擇:
            // 1\. 向服務(wù)端發(fā)送新的數(shù)據(jù)
//            att.setReadMode(false);
//            buffer.clear();
//            String newMsg = "new message from client";
//            byte[] data = newMsg.getBytes(Charset.forName("UTF-8"));
//            buffer.put(data);
//            buffer.flip();
//            att.getClient().write(buffer, att, this);
            // 2\. 關(guān)閉連接
            try {
                att.getClient().close();
            } catch (IOException e) {
            }
        } else {
            // 寫操作完成后,會進到這里
            att.setReadMode(true);
            buffer.clear();
            att.getClient().read(buffer, att, this);
        }
    }
    @Override
    public void failed(Throwable t, Attachment att) {
        System.out.println("

以上代碼都是可以運行調(diào)試的,如果讀者碰到問題,請在評論區(qū)留言。

Asynchronous Channel Groups

為了知識的完整性,有必要對 group 進行介紹,其實也就是介紹 AsynchronousChannelGroup 這個類。之前我們說過,異步 IO 一定存在一個線程池,這個線程池負責(zé)接收任務(wù)、處理 IO 事件、回調(diào)等。這個線程池就在 group 內(nèi)部,group 一旦關(guān)閉,那么相應(yīng)的線程池就會關(guān)閉。

AsynchronousServerSocketChannels 和 AsynchronousSocketChannels 是屬于 group 的,當(dāng)我們調(diào)用 AsynchronousServerSocketChannel 或 AsynchronousSocketChannel 的 open() 方法的時候,相應(yīng)的 channel 就屬于默認的 group,這個 group 由 JVM 自動構(gòu)造并管理。

如果我們想要配置這個默認的 group,可以在 JVM 啟動參數(shù)中指定以下系統(tǒng)變量:

  • java.nio.channels.DefaultThreadPool.threadFactory

    此系統(tǒng)變量用于設(shè)置 ThreadFactory,它應(yīng)該是 java.util.concurrent.ThreadFactory 實現(xiàn)類的全限定類名。一旦我們指定了這個 ThreadFactory 以后,group 中的線程就會使用該類產(chǎn)生。

  • java.nio.channels.DefaultThreadPool.initialSize

    此系統(tǒng)變量也很好理解,用于設(shè)置線程池的初始大小。

可能你會想要使用自己定義的 group,這樣可以對其中的線程進行更多的控制,使用以下幾個方法即可:

  • AsynchronousChannelGroup.withCachedThreadPool(ExecutorService executor, int initialSize)

  • AsynchronousChannelGroup.withFixedThreadPool(int nThreads, ThreadFactory threadFactory)

  • AsynchronousChannelGroup.withThreadPool(ExecutorService executor)

熟悉線程池的讀者對這些方法應(yīng)該很好理解,它們都是 AsynchronousChannelGroup 中的靜態(tài)方法。

至于 group 的使用就很簡單了,代碼一看就懂:

AsynchronousChannelGroup group = AsynchronousChannelGroup
        .withFixedThreadPool(10, Executors.defaultThreadFactory());
AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(group);
AsynchronousSocketChannel client = AsynchronousSocketChannel.open(group);

AsynchronousFileChannels 不屬于 group。但是它們也是關(guān)聯(lián)到一個線程池的,如果不指定,會使用系統(tǒng)默認的線程池,如果想要使用指定的線程池,可以在實例化的時候使用以下方法:

public static AsynchronousFileChannel open(Path file,
                                           Set options,
                                           ExecutorService executor,
                                           FileAttribute... attrs) {
    ...
}

到這里,異步 IO 就算介紹完成了。

感謝各位的閱讀,以上就是“Java非阻塞IO和異步IO的詳細介紹”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Java非阻塞IO和異步IO的詳細介紹這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!


本文題目:Java非阻塞IO和異步IO的詳細介紹
鏈接地址:
http://weahome.cn/article/gcjees.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部