亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java?NIO?通道概念選擇器使用示例詳解

 更新時(shí)間:2023年10月27日 10:50:29   作者:lane  
這篇文章主要為大家介紹了Java?NIO?通道概念選擇器使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

Java NIO通道

通道相當(dāng)于一個(gè)傳遞物品的管子,兩邊都可以往對(duì)面?zhèn)鬟f東西。

有哪些通道?

對(duì)應(yīng)文件IO和網(wǎng)絡(luò)IO,通道也分為一個(gè)FileChannel和三個(gè)socket通道(SocketChannel、ServerSocketChannel和DatagramChannel)

基礎(chǔ)一般情況下,一個(gè)通道必然關(guān)聯(lián)著一個(gè)文件描述符或者是文件句柄。

通道可以是單向的,也可以是雙向的(讀寫)。

socket通道可以是阻塞的或非阻塞的,F(xiàn)ileChannel只支持阻塞模式。

Scatter和Gather 發(fā)散和匯聚

從字面理解,通道支持多個(gè)緩沖區(qū)同時(shí)讀寫。這樣能夠充分利用現(xiàn)代操作系統(tǒng)多核CPU功能,同時(shí)填充或排干多個(gè)緩沖區(qū)。

Scatter/Gather是一個(gè)簡(jiǎn)單卻強(qiáng)大的概念,它是指在多個(gè)緩沖區(qū)上實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 I/O 操作。對(duì)于一個(gè) write 操作而言,數(shù)據(jù)是從幾個(gè)緩沖區(qū)按順序抽?。ǚQ為 gather)并沿著通道發(fā)送的。

緩沖區(qū)本身并不需要具備這種gather 的能力(通常它們也沒(méi)有此能力)。該 gather 過(guò)程的效果就好比全部緩沖區(qū)的內(nèi)容被連結(jié)起來(lái),并在發(fā)送數(shù)據(jù)前存放到一個(gè)大的緩沖區(qū)中。對(duì)于 read 操作而言,從
通道讀取的數(shù)據(jù)會(huì)按順序被散布(稱為 scatter)到多個(gè)緩沖區(qū),將每個(gè)緩沖區(qū)填滿直至通道中的數(shù)據(jù)或者緩沖區(qū)的最大空間被消耗完。

大多數(shù)現(xiàn)代操作系統(tǒng)都支持本地矢量 I/O(native vectored I/O)。當(dāng)您在一個(gè)通道上請(qǐng)求一個(gè)Scatter/Gather 操作時(shí),該請(qǐng)求會(huì)被翻譯為適當(dāng)?shù)谋镜卣{(diào)用來(lái)直接填充或抽取緩沖區(qū)。這是一個(gè)很大
的進(jìn)步,因?yàn)闇p少或避免了緩沖區(qū)拷貝和系統(tǒng)調(diào)用。Scatter/Gather 應(yīng)該使用直接的 ByteBuffers 以從本地 I/O 獲取最大性能優(yōu)勢(shì)。

Java NIO 選擇器

從最基礎(chǔ)的層面上來(lái)看,選擇器提供了問(wèn)詢通道是否就緒操作I/O的能力,選擇器可以監(jiān)控注冊(cè)在上面的多個(gè)通道,通道注冊(cè)時(shí)會(huì)返回選擇鍵(記錄通道與選擇器之間的關(guān)聯(lián)關(guān)系),選擇器管理者這些注冊(cè)的鍵、和就緒狀態(tài)鍵的集合

SelectableChannel

所有繼承SelectableChannel的通道都可以在選擇器中注冊(cè),F(xiàn)ileChannel沒(méi)有繼承這個(gè)類,所以無(wú)法使用選擇器

選擇鍵(SelectionKey)

選擇鍵是選擇器的重點(diǎn)內(nèi)容,選擇器就緒的通道通過(guò)返回選擇鍵集合來(lái)通知

public abstract class SelectionKey {
    public static final int OP_READ
    public static final int OP_WRITE
    public static final int OP_CONNECT
    public static final int OP_ACCEPT
    public abstract SelectableChannel channel();
    public abstract Selector selector();
    public abstract void cancel();
    public abstract boolean isValid();
    public abstract int interestOps();
    public abstract void interestOps(int ops);
    public abstract int readyOps();
    public final boolean isReadable()
    public final boolean isWritable()
    public final boolean isConnectable()
    public final boolean isAcceptable()
    public final Object attach(Object ob)
    public final Object attachment()
}

選擇鍵維護(hù)了通道和選擇器之間的關(guān)聯(lián),可以通過(guò)選擇鍵獲取Channel或Selector,鍵對(duì)象表示一種特殊的關(guān)聯(lián)關(guān)系,當(dāng)這種關(guān)系需要終止時(shí),可以調(diào)用cancel()方法取消,調(diào)用這個(gè)方法時(shí),不會(huì)立即被取消,而是將這個(gè)鍵放到被取消的集合里,當(dāng)Selector下次調(diào)用select()方法時(shí)會(huì)真正被清理掉。當(dāng)通道關(guān)閉時(shí),選擇鍵會(huì)自動(dòng)被取消,當(dāng)選擇器關(guān)閉時(shí),所有鍵都會(huì)被清理掉。

一個(gè)選擇器鍵包含有兩個(gè)準(zhǔn)備好的操作集合,包括感興趣的事件集合instrest和就緒的操作集合ready,通過(guò)掩碼保存

感興趣的事件集合interestOps()

通常一個(gè)鍵的instrest注冊(cè)時(shí)就已經(jīng)確認(rèn),但是可以在注冊(cè)后通過(guò)interestOps(newOps)傳入一個(gè)新的ops來(lái)改變這個(gè)值

channel.register(this.selector, SelectionKey.OP_READ);

上面的代碼注冊(cè)的鍵interest包含read事件,可以在對(duì)通道IO異步處理時(shí),改變這個(gè)ops來(lái)臨時(shí)取消對(duì)read事件的關(guān)注,以防止重復(fù)處理未處理完的通道

就緒的操作集合readyOps()

通過(guò)這個(gè)方法返回就緒的操作,isReadable( ),isWritable( ),isConnectable( ), 和isAcceptable( )用來(lái)判斷這些操作是否就緒,進(jìn)行下一步的處理

示范選擇器的使用

下面列舉兩個(gè)示例來(lái)示范選擇器的使用

單選擇器單線程

public abstract class AbstractNioServer {
    protected final static String CHARSET = "utf-8";
 protected String ip;
 protected Integer port;
 protected Selector selector;
 public AbstractNioServer(String ip, Integer port) {
        this.ip = ip;
 this.port = port;
 }
    /**
 * 客戶端連接請(qǐng)求
 *
 * @param key
 */
 protected abstract void accept(SelectionKey key) throws IOException;
 /**
 * 讀取數(shù)據(jù)
 *
 * @param key
 */
 protected abstract void read(SelectionKey key) throws IOException;
 /**
 * 初始化服務(wù)器
 *
 * @throws IOException
 */ public void init() throws IOException {
        //設(shè)置服務(wù)器地址端口
 SocketAddress address = new InetSocketAddress(this.ip, this.port);
 //創(chuàng)建服務(wù)端通道
 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
 //綁定服務(wù)器地址
 serverSocketChannel.bind(address);
 //設(shè)置為非阻塞模式
 serverSocketChannel.configureBlocking(false);
 //創(chuàng)建一個(gè)選擇器
 this.selector = Selector.open();
 //將服務(wù)器通道注冊(cè)到選擇器中,ServerSocketChannel只支持accept事件注冊(cè),validOps返回16
 serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT);
 }
    public void start() throws IOException {
        this.init();
 while (true) {
            int count = this.selector.select();
 if (count == 0) {
                //沒(méi)有就緒的選擇鍵
 continue;
 }
            Iterator<SelectionKey> iterator = this.selector.selectedKeys().iterator();
 while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
 if (!key.isValid()) {
                    continue;
 }
                if (key.isAcceptable()) {
                    //連接請(qǐng)求
 accept(key);
 } else if (key.isReadable()) {
                    //修改鍵的感興趣事件,防止被 select 重復(fù)調(diào)用,處理完事件后及時(shí)恢復(fù)
 key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));
 //讀取消息
 read(key);
 }
                iterator.remove();
 }
        }
    }
    /**
 * 恢復(fù)鍵的感興趣事件
 * @param key
 */
 protected void resumeInterOpsRead(SelectionKey key) {
        //還原key的感興趣事件
 key.interestOps(key.interestOps() | SelectionKey.OP_READ);
 //喚醒selector的select事件
 key.selector().wakeup();
 }
}
public class SingleNioServer extends AbstractNioServer {
    public static void main(String[] args) {
        SingleNioServer server = new SingleNioServer("127.0.0.1", 1008);
 try {
            server.start();
 } catch (IOException e) {
            e.printStackTrace();
 }
    }
    public SingleNioServer(String ip, Integer port) {
        super(ip, port);
 }
    @Override
 protected void accept(SelectionKey key) throws IOException {
        ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
 //SocketChannel支持 read、write、connect 事件注冊(cè),validOps返回13=1+4+8
 SocketChannel channel = serverChannel.accept();
 if (channel == null) {
            return;
 }
        System.out.println("新的連接請(qǐng)求");
 channel.configureBlocking(false);
 //如果是阻塞通道進(jìn)行注冊(cè),會(huì)拋出 IllegalBlockingModeException 異常
 channel.register(this.selector, SelectionKey.OP_READ);
 }
    @Override
 protected void read(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
 try {
            ByteBuffer buffer = ByteBuffer.allocate(1024);
 int len = channel.read(buffer);
 buffer.flip();
 if (len > 0) {
                String str = Charset.forName(CHARSET).decode(buffer).toString();
 System.out.println("客戶端消息:" + str);
 String msg = "消息已收到";
 byte[] sendData = msg.getBytes(CHARSET);
 ByteBuffer sendBuffer = ByteBuffer.wrap(sendData);
 channel.write(sendBuffer);
 super.resumeInterOpsRead(key);
 } else if (len == -1) {
                System.out.println("socket client close");
 key.cancel();
 channel.close();
 }
        } catch (IOException ex) {
            key.cancel();
 channel.close();
 }
    }
}

單選擇器多線程

public class MulitpleNioServer extends AbstractNioServer {
    public static void main(String[] args) throws IOException {
        MulitpleNioServer server = new MulitpleNioServer("127.0.0.1", 1008);
 server.start();
 }
    /**
 * 線程池
 */
 private ExecutorService executorService = Executors.newFixedThreadPool(5);
 public MulitpleNioServer(String ip, Integer port) {
        super(ip, port);
 }
    @Override
 protected void accept(SelectionKey key) throws IOException {
        ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
 //SocketChannel支持 read、write、connect 事件注冊(cè),validOps返回13=1+4+8
 SocketChannel channel = serverChannel.accept();
 System.out.println("新的連接請(qǐng)求");
 channel.configureBlocking(false);
 channel.register(this.selector, SelectionKey.OP_READ);
 }
    @Override
 protected void read(SelectionKey key) throws IOException {
        executorService.submit(new Runnable() {
            @Override
 public void run() {
                readData(key);
 }
        });
 }
    private void readData(SelectionKey key) {
        SocketChannel channel = (SocketChannel) key.channel();
 try {
            ByteBuffer buffer = ByteBuffer.allocate(1024);
 if (channel.isOpen()) {
                if (channel.isConnected()) {
                    int len = channel.read(buffer);
 buffer.flip();
 if (len > 0) {
                        String str = Charset.forName(CHARSET).decode(buffer).toString();
 System.out.println("客戶端消息:" + str);
 String msg = "消息已收到";
 byte[] sendData = msg.getBytes(CHARSET);
 ByteBuffer sendBuffer = ByteBuffer.wrap(sendData);
 channel.write(sendBuffer);
 } else if (len == -1) {
                        System.out.println("socket client close1");
 key.cancel();
 channel.close();
 }
                    super.resumeInterOpsRead(key);
 }
            }
        } catch (IOException ex) {
            System.out.println("client is close2");
 key.cancel();
 try {
                channel.close();
 } catch (IOException e) {
                e.printStackTrace();
 }
        }
    }
}

以上就是Java NIO 通道概念選擇器使用示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Java NIO 通道選擇器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論