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

Java 基礎(chǔ)之NIO 學(xué)習(xí)詳解

 更新時(shí)間:2021年09月13日 10:16:09   作者:IT__learning  
這篇文章主要介紹了java基礎(chǔ)之NIO介紹及使用,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下

一、NIO 簡介

java.nio 全稱 java non-blocking IO,是指 JDK 提供的新 API。從 JDK1.4 開始,Java 提供了一系列改進(jìn)的輸入/輸出的新特性,被統(tǒng)稱為 NIO(即 New IO)。新增了許多用于處理輸入輸出的類,這些類都被放在 java.nio 包及子包下,并且對(duì)原 java.io 包中的很多類進(jìn)行改寫,新增了滿足 NIO 的功能。

在這里插入圖片描述

NIO 以塊的方式處理數(shù)據(jù),塊 IO 的效率比流 IO 的效率高很多?!?br />

NIO 是非阻塞的,同時(shí)實(shí)現(xiàn)了 IO 多路復(fù)用。NIO 中用戶線程不會(huì)被讀寫操作阻塞住,它可以繼續(xù)干事情,所以 NIO 是可以做到用一個(gè)線程來處理多個(gè)操作的,使用它可以提供非阻塞的高伸縮性網(wǎng)絡(luò)。

在這里插入圖片描述

1、NIO 三大核心

NIO 主要有三大核心:Channel(通道)Buffer(緩沖區(qū))、Selector(選擇器)。

NIO 是基于 Channel 和緩沖區(qū)進(jìn)行操作的,數(shù)據(jù)是從通道讀取到緩沖區(qū),或者是緩沖區(qū)寫入到通道中。Selector(選擇區(qū))用于監(jiān)聽多個(gè)通道的事件(比如:連接請(qǐng)求、數(shù)據(jù)到達(dá)等),使用單個(gè)線程就可以監(jiān)聽到多個(gè)客戶端通道。

在這里插入圖片描述

(1)緩沖區(qū) Buffer

緩沖區(qū)就是用來存放具體要被操作和傳輸?shù)臄?shù)據(jù)。

緩沖區(qū)實(shí)際上是一個(gè)容器對(duì)象,更直接的說,其實(shí)就是一個(gè)數(shù)組,在 NIO 庫中,所有數(shù)據(jù)都是用緩沖區(qū)處理的。在讀取數(shù)據(jù)時(shí),它是直接讀到緩沖區(qū)中的; 在寫入數(shù)據(jù)時(shí),它也是寫入到緩沖區(qū)中的;任何時(shí)候訪問 NIO 中的數(shù)據(jù),都是將它放到緩沖區(qū)中。Channel 提供從文件、網(wǎng)絡(luò)讀取數(shù)據(jù)的渠道,但是讀取或?qū)懭氲臄?shù)據(jù)都必須經(jīng)由 Buffer,如下圖所示:

在這里插入圖片描述

在 NIO 中,所有的緩沖區(qū)類型都繼承于抽象類 Buffer,最常用的就是 ByteBuffer,對(duì)于 Java 中的基本類型,基本都有一個(gè)具體 Buffer 類型與之相對(duì)應(yīng),它們之間的繼承關(guān)系如下圖所示:

在這里插入圖片描述

Buffer 的基本原理:

緩沖區(qū)對(duì)象本質(zhì)上是一個(gè)數(shù)組,但它其實(shí)是一個(gè)特殊的數(shù)組,緩沖區(qū)對(duì)象內(nèi)置了一些機(jī)制,能夠跟蹤和記錄緩沖區(qū)的狀態(tài)變化情況,如果使用 get()方法從緩沖區(qū)獲取數(shù)據(jù)或者使用 put()方法把數(shù)據(jù)寫入緩沖區(qū),都會(huì)引起緩沖區(qū)狀態(tài)的變化。 在緩沖區(qū)中,最重要的屬性有下面三個(gè),它們一起合作完成對(duì)緩沖區(qū)內(nèi)部狀態(tài)的變化跟蹤:

1)position:指定下一個(gè)將要被寫入或者讀取的元素索引,它的值由 get()/put()方法自動(dòng)更新,在新創(chuàng)建一個(gè) Buffer 對(duì)象時(shí),position 被初始化為 0。

2)limit:指定還有多少數(shù)據(jù)需要取出(在從緩沖區(qū)寫入通道時(shí)),或者還有多少空間可以放入數(shù)據(jù)(在從通道讀入緩沖區(qū)時(shí))。

3)capacity:指定了可以存儲(chǔ)在緩沖區(qū)中的最大數(shù)據(jù)容量,實(shí)際上,它指定了底層數(shù)組的大小,或者至少是指定了準(zhǔn)許我們使用的底層數(shù)組的容量。

以上三個(gè)屬性值之間有一些相對(duì)大小的關(guān)系:0 <= position <= limit <= capacity。如果我們創(chuàng)建一個(gè)新的容量大小為20 的 ByteBuffer 對(duì)象,在初始化的時(shí)候,position 設(shè)置為 0,limit 和 capacity 被設(shè)置為 10,在以后使用 ByteBuffer對(duì)象過程中,capacity 的值不會(huì)再發(fā)生變化,而其它兩個(gè)個(gè)將會(huì)隨著使用而變化。

(2)通道 Channel

通道 Channel 就是數(shù)據(jù)傳輸用的通道,作用是打開到IO設(shè)備的連接、文件或套接字。

通道是一個(gè)對(duì)象,通過它可以讀取和寫入數(shù)據(jù),當(dāng)然了所有數(shù)據(jù)都通過 Buffer 對(duì)象來處理。不會(huì)將字節(jié)直接寫入通道中,相反是將數(shù)據(jù)寫入包含一個(gè)或者多個(gè)字節(jié)的緩沖區(qū)。同樣不會(huì)直接從通道中讀取字節(jié),而是將數(shù)據(jù)從通道讀入緩沖區(qū),再從緩沖區(qū)獲取這個(gè)字節(jié)。

在 NIO 中,提供了多種通道對(duì)象,而所有的通道對(duì)象都實(shí)現(xiàn)了 Channel 接口。

在這里插入圖片描述

FileChannel、DatagramChannel 用于 UDP 的數(shù)據(jù)讀寫;

ServerSocketChannel和SocketChannel 用于 TCP 的數(shù)據(jù)讀寫;

(3)Selector 選擇器

能夠檢測(cè)多個(gè)注冊(cè)的通道上是否有事件發(fā)生,如果有事件發(fā)生,便獲取事件然后針對(duì)每個(gè)事件進(jìn)行相應(yīng)的處理。這樣就可以只用一個(gè)單線程去管理多個(gè)通道,也就是管理多個(gè)連接。這樣使得只有在連接真正有讀寫事件發(fā)生時(shí),才會(huì)調(diào)用函數(shù)來進(jìn)行讀寫,就大大地減少了系統(tǒng)開銷,并且不必為每個(gè)連接都創(chuàng)建一個(gè)線程,不用去維護(hù)多個(gè)線程,并且避免了多線程之間的上下文切換導(dǎo)致的開銷。

在這里插入圖片描述

NIO 中實(shí)現(xiàn)非阻塞 I/O 的核心對(duì)象就是 Selector,Selector 就是注冊(cè)各種 I/O 事件的地方,而且當(dāng)那些事件發(fā)生時(shí),就是這個(gè)對(duì)象告訴我們所發(fā)生的事件,如下圖所示:

在這里插入圖片描述

從圖中可以看出,當(dāng)有讀或?qū)懙热魏巫?cè)的事件發(fā)生時(shí),可以從 Selector 中獲得相應(yīng)的 SelectionKey,同時(shí)從 SelectionKey 中可以找到發(fā)生的事件和該事件所發(fā)生的具體的 SelectableChannel,以獲得客戶端發(fā)送過來的數(shù)據(jù)。

2、NIO 和 IO 的區(qū)別

在這里插入圖片描述

面向目標(biāo)不同

NIO 和傳統(tǒng) IO(一下簡稱IO)之間第一個(gè)最大的區(qū)別是,IO 是面向流的,NIO 是面向緩沖區(qū)的。 Java IO 面向流意味著每次從流中讀一個(gè)或多個(gè)字節(jié),直至讀取所有字節(jié),它們沒有被緩存在任何地方。此外,它不能前后移動(dòng)流中的數(shù)據(jù)。如果需要前后移動(dòng)從流中讀取的數(shù)據(jù),需要先將它緩存到一個(gè)緩沖區(qū)。NIO 的緩沖導(dǎo)向方法略有不同。數(shù)據(jù)讀取到一個(gè)它稍后處理的緩沖區(qū),需要時(shí)可在緩沖區(qū)中前后移動(dòng)。這就增加了處理過程中的靈活性。但是,NIO 還需要檢查是否該緩沖區(qū)中包含所有您需要處理的數(shù)據(jù)。而且,需確保當(dāng)更多的數(shù)據(jù)讀入緩沖區(qū)時(shí),不要覆蓋緩沖區(qū)里尚未處理的數(shù)據(jù)。

阻塞/非阻塞

IO 的各種流是阻塞的。這意味著,當(dāng)一個(gè)線程調(diào)用 read() 或 write() 時(shí),該線程被阻塞,直到有一些數(shù)據(jù)被讀取,或數(shù)據(jù)完全寫入。該線程在此期間不能再干任何事情了。
NIO 是非阻塞模式,使一個(gè)線程從某通道發(fā)送請(qǐng)求讀取數(shù)據(jù),但是它僅能得到目前可用的數(shù)據(jù),如果目前沒有數(shù)據(jù)可用時(shí),就什么都不會(huì)獲取。而不是保持線程阻塞,所以直至數(shù)據(jù)變的可以讀取之前,該線程可以繼續(xù)做其他的事情。 線程通常將非阻塞 IO 的空閑時(shí)間用于在其它通道上執(zhí)行 IO 操作,所以一個(gè)單獨(dú)的線程現(xiàn)在可以管理多個(gè)輸入和輸出通道。

二、NIO 的 API

1、Selector

Selector 可以同時(shí)監(jiān)控多個(gè) SelectableChannel 的 IO 狀況,是非阻塞 IO 的核心。

public abstract class Selector implements Closeable {
    protected Selector() { }
	// 創(chuàng)建 Selector 實(shí)例
    public static Selector open() throws IOException {
        return SelectorProvider.provider().openSelector();
    }
    public abstract boolean isOpen();
    public abstract SelectorProvider provider();
	//  key set 代表了所有注冊(cè)在這個(gè) Selector 上的 channel ,這個(gè)集合可以通過 keys() 方法拿到。
    public abstract Set<SelectionKey> keys();
	// Selected-key set 代表了所有通過 select() 方法監(jiān)測(cè)到可以進(jìn)行 IO 操作的 channel ,這個(gè)集合可以通過 selectedKeys() 拿到。
    public abstract Set<SelectionKey> selectedKeys();
    public abstract int selectNow() throws IOException;
	// 可以設(shè)置超時(shí)的 select() 操作。
    public abstract int select(long timeout) throws IOException;
	// 監(jiān)控所有注冊(cè)的 channel ,當(dāng)其中有注冊(cè)的 IO 操作可以進(jìn)行時(shí),該函數(shù)返回,并將對(duì)應(yīng)的 SelectionKey 加入 selected-key set 。
    public abstract int select() throws IOException;
	// 使一個(gè)還未返回的 select() 操作立刻返回。
    public abstract Selector wakeup();
    public abstract void close() throws IOException;
}

2、Buffer

在這里插入圖片描述

Buffer 定義了一個(gè)可以線性存放 primitive type 數(shù)據(jù)的容器接口。 Buffer 主要包含了與類型( byte, char… )無關(guān)的功能。值得注意的是 Buffer 及其子類都不是線程安全的。

public abstract class Buffer {
    static final int SPLITERATOR_CHARACTERISTICS =
        Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;
    // Invariants: mark <= position <= limit <= capacity
    private int mark = -1;		// 一個(gè)臨時(shí)存放的位置下標(biāo)。調(diào)用 mark() 會(huì)將 mark 設(shè)為當(dāng)前的 position 的值,以后調(diào)用 reset() 會(huì)將 position 屬性設(shè)置為 mark 的值。 mark 的值總是小于等于 position 的值,如果將 position 的值設(shè)的比 mark 小,當(dāng)前的 mark 值會(huì)被拋棄掉。
    private int position = 0;		// 讀 / 寫操作的當(dāng)前下標(biāo)。當(dāng)使用 buffer 的相對(duì)位置進(jìn)行讀 / 寫操作時(shí),讀 / 寫會(huì)從這個(gè)下標(biāo)進(jìn)行,并在操作完成后, buffer 會(huì)更新下標(biāo)的值。
    private int limit;		// 在 Buffer 上進(jìn)行的讀寫操作都不能越過這個(gè)下標(biāo)。當(dāng)寫數(shù)據(jù)到 buffer 中時(shí), limit 一般和 capacity 相等,當(dāng)讀數(shù)據(jù)時(shí), limit 代表 buffer 中有效數(shù)據(jù)的長度。
    private int capacity;		// 這個(gè) Buffer 最多能放多少數(shù)據(jù)。 capacity 一般在 buffer 被創(chuàng)建的時(shí)候指定
    long address;
    Buffer(int mark, int pos, int lim, int cap) {       // package-private
        if (cap < 0)
            throw new IllegalArgumentException("Negative capacity: " + cap);
        this.capacity = cap;
        limit(lim);
        position(pos);
        if (mark >= 0) {
            if (mark > pos)
                throw new IllegalArgumentException("mark > position: ("
                                                   + mark + " > " + pos + ")");
            this.mark = mark;
        }
    }
    public final int capacity() {
        return capacity;
    }
    public final int position() {
        return position;
    }
    public final Buffer position(int newPosition) {
        if ((newPosition > limit) || (newPosition < 0))
            throw new IllegalArgumentException();
        position = newPosition;
        if (mark > position) mark = -1;
        return this;
    }
    public final int limit() {
        return limit;
    }
    public final Buffer limit(int newLimit) {
        if ((newLimit > capacity) || (newLimit < 0))
            throw new IllegalArgumentException();
        limit = newLimit;
        if (position > limit) position = limit;
        if (mark > limit) mark = -1;
        return this;
    }
    public final Buffer mark() {
        mark = position;
        return this;
    }
	// 
    public final Buffer reset() {
        int m = mark;
        if (m < 0)
            throw new InvalidMarkException();
        position = m;
        return this;
    }
	// 把 position 設(shè)為 0 ,把 limit 設(shè)為 capacity ,一般在把數(shù)據(jù)寫入 Buffer 前調(diào)用。
    public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }
	// 把 limit 設(shè)為當(dāng)前 position ,把 position 設(shè)為 0 ,一般在從 Buffer 讀出數(shù)據(jù)前調(diào)用。
    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }
	// 把 position 設(shè)為 0 , limit 不變,一般在把數(shù)據(jù)重寫入 Buffer 前調(diào)用。
    public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }
    public final int remaining() {
        return limit - position;
    }
    public final boolean hasRemaining() {
        return position < limit;
    }
	// 用來判斷一個(gè) Buffer 是否只讀
    public abstract boolean isReadOnly();
    public abstract boolean hasArray();
    public abstract Object array();
    public abstract int arrayOffset();
    public abstract boolean isDirect();
    final int nextGetIndex() {                          // package-private
        if (position >= limit)
            throw new BufferUnderflowException();
        return position++;
    }
    final int nextGetIndex(int nb) {                    // package-private
        if (limit - position < nb)
            throw new BufferUnderflowException();
        int p = position;
        position += nb;
        return p;
    }
    final int nextPutIndex() {                          // package-private
        if (position >= limit)
            throw new BufferOverflowException();
        return position++;
    }
    final int nextPutIndex(int nb) {                    // package-private
        if (limit - position < nb)
            throw new BufferOverflowException();
        int p = position;
        position += nb;
        return p;
    }
    final int checkIndex(int i) {                       // package-private
        if ((i < 0) || (i >= limit))
            throw new IndexOutOfBoundsException();
        return i;
    }
    final int checkIndex(int i, int nb) {               // package-private
        if ((i < 0) || (nb > limit - i))
            throw new IndexOutOfBoundsException();
        return i;
    }
    final int markValue() {                             // package-private
        return mark;
    }
    final void truncate() {                             // package-private
        mark = -1;
        position = 0;
        limit = 0;
        capacity = 0;
    }
    final void discardMark() {                          // package-private
        mark = -1;
    }
    static void checkBounds(int off, int len, int size) { // package-private
        if ((off | len | (off + len) | (size - (off + len))) < 0)
            throw new IndexOutOfBoundsException();
    }
}

Buffer子類(以下以ByteBuffer為例子)通用方法:

1)ByteBuffer(int N) :構(gòu)造方法,設(shè)定緩沖區(qū)大小為N個(gè)byte大小的空間 ;
2)byte[] get():讀取buffer中的所有數(shù)據(jù);
3)void put(byte[]):數(shù)據(jù)寫入buffer【功能和從channel中讀取數(shù)據(jù)到buffer中一樣】;
4)void filp():切換模式(寫模式->讀模式);
5)void rewind():重讀buffer中的數(shù)據(jù)(position重置為0);
6)void clear():清空。重置所有指針,不刪除數(shù)據(jù)!!(position=0,limit=capacity,重新供寫入);
7)void compact():半清空,保留仍未讀取的數(shù)據(jù)。(position=最后一個(gè)未讀單元之后的位置,limit=cap,重新供寫入);
8)mark():標(biāo)記時(shí)刻A的當(dāng)前pos【與reset()一起用】 reset():回到時(shí)刻A時(shí)標(biāo)記的pos位置;
9)close():關(guān)閉并釋放channel對(duì)象。;

3、Package java.nio.channels

Channel 是一個(gè)可以進(jìn)行 IO 操作的通道(比如,通過 FileChannel ,我們可以對(duì)文件進(jìn)行讀寫操作)。 java.nio.channels 包含了文件系統(tǒng)和網(wǎng)絡(luò)通訊相關(guān)的 channel 類。這個(gè)包通過 Selector 和SelectableChannel 這兩個(gè)類,還定義了一個(gè)進(jìn)行非阻塞( non-blocking ) IO 操作的 API ,這對(duì)需要高性能 IO 的應(yīng)用非常重要。

(1) java.nio.channels 中 interface 的關(guān)系:

在這里插入圖片描述

1)Channel

Channel 表現(xiàn)了一個(gè)可以進(jìn)行 IO 操作的通道,該 interface 定義了以下方法:

boolean isOpen()	// 該 Channel 是否是打開的。 
void close()		// 關(guān)閉這個(gè) Channel ,相關(guān)的資源會(huì)被釋放。 

2)ReadableByteChannel

定義了一個(gè)可從中讀取 byte 數(shù)據(jù)的 channel interface 。

int read(ByteBuffer dst) 	// 從 channel 中讀取 byte 數(shù)據(jù)并寫到 ByteBuffer 中。返回讀取的 byte 數(shù)。

3)WritableByteChannel

定義了一個(gè)可向其寫 byte 數(shù)據(jù)的 channel interface 。

int write(ByteBuffer src)	// 從 ByteBuffer 中讀取 byte 數(shù)據(jù)并寫到 channel 中。返回寫出的 byte 數(shù)。 

4)ByteChannel

ByteChannel 并沒有定義新的方法,它的作用只是把 ReadableByteChannel 和 WritableByteChannel 合并在一起。

5)ScatteringByteChannel

繼承了 ReadableByteChannel 并提供了同時(shí)往幾個(gè) ByteBuffer 中寫數(shù)據(jù)的能力。

6)GatheringByteChannel

繼承了 WritableByteChannel 并提供了同時(shí)從幾個(gè) ByteBuffer 中讀數(shù)據(jù)的能力。

7)InterruptibleChannel

用來表現(xiàn)一個(gè)可以被異步關(guān)閉的 Channel 。

(2)java.nio.channels 中類的關(guān)系:

在這里插入圖片描述

1)非阻塞 IO 的支持可以算是 NIO API 中最重要的功能,非阻塞 IO 允許應(yīng)用程序同時(shí)監(jiān)控多個(gè) channel 以提高性能,這一功能是通過 Selector , SelectableChannel 和 SelectionKey 這 3 個(gè)類來實(shí)現(xiàn)的。

2)SelectableChannel 抽象類是所有支持非阻塞 IO 操作的 channel (如 DatagramChannel 、 SocketChannel )的父類。 SelectableChannel 可以注冊(cè)到一個(gè)或多個(gè) Selector 上以進(jìn)行非阻塞 IO 操作。

3)SelectableChannel 可以是 blocking 和 non-blocking 模式(所有 channel 創(chuàng)建的時(shí)候都是 blocking 模式),只有 non-blocking 的 SelectableChannel 才可以參與非阻塞 IO 操作。

4)Selector 這個(gè)類通過 select() 函數(shù),給應(yīng)用程序提供了一個(gè)可以同時(shí)監(jiān)控多個(gè)IO channel 的方法。

5)Channel 的相關(guān)實(shí)現(xiàn)類:FileChannel、SocketChannel與ServerSocketChannel、DatagramChannel,分別對(duì)應(yīng):“文件操作通道”、“TCP通信操作通道”、“UDP通信操作通道”。這幾個(gè)實(shí)現(xiàn)類中,除了 FileChannel 不能進(jìn)入非阻塞狀態(tài),其他實(shí)現(xiàn)類都可以進(jìn)入非阻塞狀態(tài)。

(3)SelectableChannel 接口

public abstract class SelectableChannel extends AbstractInterruptibleChannel implements Channel
{
    protected SelectableChannel() { }
    public abstract SelectorProvider provider();
	// 返回一個(gè) bit mask ,表示這個(gè) channel 上支持的 IO 操作。當(dāng)前在 SelectionKey 中,用靜態(tài)常量定義了 4 種 IO 操作的 bit 值: OP_ACCEPT , OP_CONNECT , OP_READ 和 OP_WRITE 。
    public abstract int validOps();
	// 該 channel 是否已注冊(cè)在一個(gè)或多個(gè) Selector 上
    public abstract boolean isRegistered();
	// 返回該 channe 在 Selector 上的注冊(cè)關(guān)系所對(duì)應(yīng)的 SelectionKey 。若無注冊(cè)關(guān)系,返回 null 。
    public abstract SelectionKey keyFor(Selector sel);
	// 多出來的 att 參數(shù)會(huì)作為 attachment 被存放在返回的 SelectionKey 中,這在需要存放一些 session state 的時(shí)候非常有用。
    public abstract SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException;
	// 將當(dāng)前 channel 注冊(cè)到一個(gè) Selector 上并返回對(duì)應(yīng)的 SelectionKey 。在這以后,通過調(diào)用 Selector 的 select() 函數(shù)就可以監(jiān)控這個(gè) channel 。 ops 這個(gè)參數(shù)是一個(gè) bit mask ,代表了需要監(jiān)控的 IO 操作。
    public final SelectionKey register(Selector sel, int ops) throws ClosedChannelException
    {
        return register(sel, ops, null);
    }
	// 設(shè)置 blocking 模式
    public abstract SelectableChannel configureBlocking(boolean block) throws IOException;
	// 返回是否為 blocking 模式
    public abstract boolean isBlocking();
    public abstract Object blockingLock();
}

(4)Channel 通用方法:

int read(Buffer):將數(shù)據(jù)從 channel 讀取到 buffer 中【讀channel,寫buffer】;
int read(Buffer[]):將數(shù)據(jù)從 channel 讀取到 buffer 數(shù)組中;
int write(Buffer):將數(shù)據(jù)從 buffer 寫入到 channel 中【讀buffer,寫channel】;
int write(Buffer[]):將數(shù)據(jù)從 buffer 數(shù)組寫入到 channel 中;

三、NIO 示例

1、TCP 通信 —— SocketChannel

使用 NIO 開發(fā)一個(gè)入門案例,實(shí)現(xiàn)服務(wù)器端和客戶端之間的數(shù)據(jù)通信(非阻塞)。

在這里插入圖片描述

(1)客戶端

public class NIOClient {
    public static void main(String[] args) throws Exception{
        //得到一個(gè)網(wǎng)絡(luò)通道
        SocketChannel socketChannel = SocketChannel.open();
        //設(shè)置非阻塞
        socketChannel.configureBlocking(false);
        //連接網(wǎng)絡(luò)
        InetSocketAddress address = new InetSocketAddress("localhost",8081);
        //判斷是否連接
        if(!socketChannel.connect(address)){
            while(!socketChannel.finishConnect()){
                System.out.println("沒有服務(wù)端進(jìn)行連接");
            }
        }
        //要發(fā)送的內(nèi)容
        String str = "hello NIO 服務(wù)端!";
        // 將要轉(zhuǎn)發(fā)的字符串內(nèi)容轉(zhuǎn)換成 Byte 類型
        ByteBuffer byteBuffer = ByteBuffer.wrap(str.getBytes());
        //寫入通道
        socketChannel.write(byteBuffer);
        System.in.read();
    }
}

(2)服務(wù)端

public class NIOServer {
    public static void main(String[] args) throws Exception{
        //得到通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //得到selector對(duì)象
        Selector selector = Selector.open();
        //設(shè)置為非阻塞
        serverSocketChannel.configureBlocking(false);
        //設(shè)置端口
        serverSocketChannel.bind(new InetSocketAddress(8081));
        //注冊(cè)到selector對(duì)象上
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while(true){
            //監(jiān)控客戶端
            if(selector.select(200)==0){
                System.out.println("沒有服務(wù)端連接");
                continue;
            }
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                //獲取所有的監(jiān)聽對(duì)象
                SelectionKey selectionKey = iterator.next();
                //連接客戶端
                if(selectionKey.isAcceptable()){
                    //得到通道
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1023));
                }
                //讀取數(shù)據(jù)
                if(selectionKey.isReadable()){
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    ByteBuffer buffer = (ByteBuffer) selectionKey.attachment();
                    socketChannel.read(buffer);
                    System.out.printf("客戶端發(fā)來的數(shù)據(jù):%s%n", new String(buffer.array()));
                }
                //刪除防止重復(fù)發(fā)送
                iterator.remove();
            }
        }
    }
}

2、文件 IO —— FileChannel

(1)讀文件

 public static byte[] readBytes(String fileName) {
      try {
          ///獲取對(duì)應(yīng)文件的FileChannel對(duì)象
          RandomAccessFile accessFile = new RandomAccessFile(fileName, "rw");
          FileChannel fileChannel = accessFile.getChannel();
          /// 創(chuàng)建一個(gè)緩沖區(qū)(大小為48byte)
          ByteBuffer byteBuffer = ByteBuffer.allocate(48);
          StringBuilder builder = new StringBuilder();
		  // 從文件向緩沖區(qū)寫入數(shù)據(jù)
          int bytesRead = fileChannel.read(byteBuffer);
          // 若讀取到該通道數(shù)據(jù)的末尾,則返回-1  
          while (bytesRead != -1) {
              System.out.println("Read " + bytesRead);
              // 由向緩沖區(qū)寫入數(shù)據(jù)轉(zhuǎn)換成從緩沖區(qū)讀取數(shù)據(jù)需要調(diào)用該方法
              byteBuffer.flip();
              ///每次讀取完之后,輸出緩存中的內(nèi)容
              while (byteBuffer.hasRemaining()) {
                  System.out.println((char) byteBuffer.get());
                  builder.append((char) byteBuffer.get());
              }
              // 然后清空緩存區(qū)
              byteBuffer.clear();
              // 重新再讀數(shù)據(jù)到緩存區(qū)中
              bytesRead = fileChannel.read(byteBuffer);
          }
          accessFile.close();
          return builder.toString().getBytes();
      } catch (IOException e) {
          e.printStackTrace();
          return null;
      }
  }

(2)寫文件

  public static void writeBytes(String fileName, byte[] data) {
      try {
          RandomAccessFile accessFile = new RandomAccessFile(fileName, "rw");
          FileChannel channel = accessFile.getChannel();
          ByteBuffer buffer = ByteBuffer.allocate(48);
          buffer.put(data);
          channel.write(buffer);
      } catch (FileNotFoundException e) {
          e.printStackTrace();
      } catch (IOException e) {
          e.printStackTrace();
      }
  }

(3)通道間內(nèi)容傳輸

  /**
   * channel 間的傳輸
   *
   * @param sFileName 源文件
   * @param dFileName 目標(biāo)文件
   */
  public static void channelToChannel(String sFileName, String dFileName) {
      try {
          RandomAccessFile sAccess = new RandomAccessFile(sFileName, "rw");
          RandomAccessFile dAccess = new RandomAccessFile(dFileName, "rw");
          FileChannel sChannel = sAccess.getChannel();
          FileChannel dChannel = dAccess.getChannel();
          long pos = 0;
          long sCount = sChannel.size();
          long dCount = dChannel.size();
//            dChannel.transferFrom(sChannel,pos,sCount);//dChannel 必須是FileChannel
          sChannel.transferTo(pos, dCount, dChannel);///sChannel 是FileChannel
      } catch (FileNotFoundException e) {
          e.printStackTrace();
      } catch (IOException e) {
          e.printStackTrace();
      }
  }

3、UDP通信 —— DatagramChannel

   /**
   * 關(guān)于:DatagramChannel
   * UDP 無連接網(wǎng)絡(luò)協(xié)議
   * 發(fā)送和接收的是數(shù)據(jù)包
   */
  public static void datagramChannel() {
      DatagramChannel datagramChannel = null;
      try {
          ///打開
          datagramChannel = DatagramChannel.open();
          ///連接并開始監(jiān)聽UDP 9999端口
          datagramChannel.socket().bind(new InetSocketAddress(9999));
          // 接收數(shù)據(jù)包(receive()方法會(huì)將接收到的數(shù)據(jù)包內(nèi)容復(fù)制到指定的Buffer. 如果Buffer容不下收到的數(shù)據(jù),多出的數(shù)據(jù)將被丟棄。 )
          ByteBuffer buf = ByteBuffer.allocate(48);
          buf.clear();
          datagramChannel.receive(buf);
          // 發(fā)送數(shù)據(jù) send()
          String sendMsg = "要發(fā)送的數(shù)據(jù)";
          ByteBuffer sendBuf = ByteBuffer.allocate(48);
          sendBuf.clear();
          sendBuf.put(sendMsg.getBytes());
          sendBuf.flip();
          datagramChannel.send(sendBuf,new InetSocketAddress("xxxxx",80));
          // TODO: 連接到特定的地址(鎖住DatagramChannel ,讓其只能從特定地址收發(fā)數(shù)據(jù) 因?yàn)閁DP無連接,本身沒有真正的連接產(chǎn)出)
          datagramChannel.connect(new InetSocketAddress("jenkov.com", 80));
          ///連接后,也可以使用Channal 的read()和write()方法,就像在用傳統(tǒng)的通道一樣。只是在數(shù)據(jù)傳送方面沒有任何保證
      } catch (IOException e) {
          e.printStackTrace();
      } finally {
          if (datagramChannel != null)
              try {
                  datagramChannel.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
      }
  }

4、NIO 管道(Pipe)

NIO Pipe,是兩個(gè)線程之間的單向連接通道(讀下圖可知)

在這里插入圖片描述

整體原理:ThreadA 中獲取的數(shù)據(jù)通過 SinkChannel 傳入(寫入)管道,當(dāng) ThreadB 要讀取 ThreadA 的數(shù)據(jù),則通過管道的 SourceChannel 傳出(讀?。?shù)據(jù)。

Pipe 類內(nèi)部有兩個(gè)成員屬性,分別是:

Pipe.SinkChannel:數(shù)據(jù)入口通道
Pipe.SourceChannel:數(shù)據(jù)出口通道

/**
   * 關(guān)于NIO管道(Pipe)
   * 定義:2個(gè)線程之間的單向數(shù)據(jù)連接
   */
  public static void aboutPipe(){
      Pipe pipe=null;
      try {
          /// 打開管道
          pipe = Pipe.open();
          ///TODO: 一、 向管道寫入數(shù)據(jù)
          /// 訪問Pipe.sinkChannel,向Pipe寫入數(shù)據(jù)
          /// 首先,獲取Pipe.sinkChannel
          Pipe.SinkChannel sinkChannel = pipe.sink();
          /// 然后,調(diào)用write(),開始寫入數(shù)據(jù)
          String newData = "New String to write to file..." + System.currentTimeMillis();
          ByteBuffer buf = ByteBuffer.allocate(48);
          buf.clear();
          buf.put(newData.getBytes());
          buf.flip();
          while(buf.hasRemaining()){
          sinkChannel.write(buf);
          }
          // TODO: 二、讀取管道中的數(shù)據(jù)
          // 首先,獲取Pipe.sourceChannel
          Pipe.SourceChannel sourceChannel = pipe.source();
          /// 讀取數(shù)據(jù)到buffer
          ByteBuffer buf2 = ByteBuffer.allocate(48);
          int bytesRead = sourceChannel.read(buf2);
      } catch (IOException e) {
          e.printStackTrace();
      }
  }

總結(jié)

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • springboot集成fastDfs過程代碼實(shí)例

    springboot集成fastDfs過程代碼實(shí)例

    這篇文章主要介紹了springboot集成fastDfs過程代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • JAVA開發(fā)環(huán)境Vs?code配置步驟詳解

    JAVA開發(fā)環(huán)境Vs?code配置步驟詳解

    這篇文章主要為大家介紹了JAVA開發(fā)環(huán)境Vs?code配置步驟詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-04-04
  • java并發(fā)編程專題(七)----(JUC)ReadWriteLock的用法

    java并發(fā)編程專題(七)----(JUC)ReadWriteLock的用法

    這篇文章主要介紹了java ReadWriteLock的用法,文中講解非常詳細(xì),示例代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • JAVA實(shí)現(xiàn)將磁盤中所有空文件夾進(jìn)行刪除的代碼

    JAVA實(shí)現(xiàn)將磁盤中所有空文件夾進(jìn)行刪除的代碼

    這篇文章主要介紹了JAVA實(shí)現(xiàn)將磁盤中所有空文件夾進(jìn)行刪除的代碼,需要的朋友可以參考下
    2017-06-06
  • 關(guān)于maven依賴 ${xxx.version}報(bào)錯(cuò)問題

    關(guān)于maven依賴 ${xxx.version}報(bào)錯(cuò)問題

    這篇文章主要介紹了關(guān)于maven依賴 ${xxx.version}報(bào)錯(cuò)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • 帶你深入了解java-代理機(jī)制

    帶你深入了解java-代理機(jī)制

    Java 有兩種代理方式,一種是靜態(tài)代理,另一種是動(dòng)態(tài)代理。如果我們?cè)诖a編譯時(shí)就確定了被代理的類是哪一個(gè),那么就可以直接使用靜態(tài)代理;如果不能確定,那么可以使用類的動(dòng)態(tài)加載機(jī)制,在代碼運(yùn)行期間加載被代理的類這就是動(dòng)態(tài)代理
    2021-08-08
  • Java中Synchronized的用法解析

    Java中Synchronized的用法解析

    synchronized是Java中的關(guān)鍵字,是一種同步鎖,本文給大家詳細(xì)介紹Java Synchronized 用法大全,感興趣的朋友跟隨小編一起看看吧
    2021-11-11
  • Activiti流程引擎對(duì)象及配置原理解析

    Activiti流程引擎對(duì)象及配置原理解析

    這篇文章主要介紹了Activiti流程引擎對(duì)象及配置原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • Java遞歸調(diào)用如何實(shí)現(xiàn)數(shù)字的逆序輸出方式

    Java遞歸調(diào)用如何實(shí)現(xiàn)數(shù)字的逆序輸出方式

    這篇文章主要介紹了Java遞歸調(diào)用如何實(shí)現(xiàn)數(shù)字的逆序輸出方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • Mybatis的@select和@SelectProvider注解方式動(dòng)態(tài)SQL語句解讀

    Mybatis的@select和@SelectProvider注解方式動(dòng)態(tài)SQL語句解讀

    這篇文章主要介紹了Mybatis的@select和@SelectProvider注解方式動(dòng)態(tài)SQL語句,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12

最新評(píng)論