欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

目錄

柚子快報(bào)激活碼778899分享:Java的NIO體系

柚子快報(bào)激活碼778899分享:Java的NIO體系

http://yzkb.51969.com/

目錄

NIO1、操作系統(tǒng)級(jí)別下的IO模型有哪些?2、Java語(yǔ)言下的IO模型有哪些?3、Java的NIO應(yīng)用場(chǎng)景?相比于IO的優(yōu)勢(shì)在哪?4、Java的IO、NIO、AIO 操作文件讀寫(xiě)5、NIO的核心類 :Buffer(緩沖區(qū))、Channel(通道)、Selector(選擇器)6、Java NIO中的零拷貝優(yōu)化支持

NIO

1、操作系統(tǒng)級(jí)別下的IO模型有哪些?

阻塞式 IO (Blocking IO): 當(dāng)應(yīng)用程序發(fā)起 IO 操作時(shí),如果數(shù)據(jù)還沒(méi)有準(zhǔn)備好或者無(wú)法立即處理,IO 操作會(huì)阻塞程序的執(zhí)行,直到數(shù)據(jù)準(zhǔn)備就緒或者操作完成為止。

非阻塞式 IO (Non-blocking IO): 在非阻塞 IO 模型中,應(yīng)用程序發(fā)起 IO 操作后,會(huì)立即返回,無(wú)論數(shù)據(jù)是否就緒或者能否立即處理。這樣程序可以繼續(xù)執(zhí)行其他任務(wù),而不必等待 IO 操作完成。需要通過(guò)輪詢或者事件通知等方式來(lái)檢查 IO 操作的狀態(tài)。

IO 復(fù)用 (IO Multiplexing): IO 復(fù)用模型通過(guò)操作系統(tǒng)提供的多路復(fù)用機(jī)制,如 select、poll 或 epoll,在一個(gè)線程中同時(shí)監(jiān)視多個(gè) IO 通道的狀態(tài)。當(dāng)其中任意一個(gè) IO 通道就緒時(shí),程序可以進(jìn)行相應(yīng)的處理。常見(jiàn)于網(wǎng)絡(luò)編程中。

信號(hào)驅(qū)動(dòng) IO (Signal-driven IO): 在信號(hào)驅(qū)動(dòng) IO 模型中,應(yīng)用程序會(huì)將 IO 操作請(qǐng)求發(fā)送給操作系統(tǒng),并注冊(cè)一個(gè)信號(hào)處理函數(shù)。當(dāng) IO 操作完成時(shí),操作系統(tǒng)會(huì)發(fā)送一個(gè)信號(hào)給應(yīng)用程序,通知其 IO 操作已完成,然后應(yīng)用程序可以調(diào)用相應(yīng)的處理函數(shù)來(lái)處理數(shù)據(jù)。

異步 IO (Asynchronous IO): 異步 IO 模型中,應(yīng)用程序發(fā)起 IO 操作后立即返回,但是會(huì)指定一個(gè)回調(diào)函數(shù)或者事件處理器。當(dāng) IO 操作完成時(shí),操作系統(tǒng)會(huì)通知應(yīng)用程序,然后調(diào)用指定的回調(diào)函數(shù)來(lái)處理數(shù)據(jù)。相比非阻塞 IO,異步 IO 不需要程序通過(guò)輪詢來(lái)檢查 IO 狀態(tài),因此效率更高。

2、Java語(yǔ)言下的IO模型有哪些?

BIO (Blocking I/O) BIO 屬于同步阻塞 IO 模型,該模型中,應(yīng)用程序發(fā)起IO操作后,會(huì)一直阻塞,直到操作系統(tǒng)內(nèi)核把數(shù)據(jù)拷貝到用戶空間。

NIO (Non-blocking I/O) Java 中的 NIO 于 JDK 1.4 中引入,對(duì)應(yīng) java.nio 包,提供了 Channel , Selector,Buffer 等抽象。 它是支持面向緩沖的,基于通道的 I/O 操作方法。 NIO適用于 高負(fù)載、高并發(fā)的(網(wǎng)絡(luò))請(qǐng)求。 Java 中的 NIO 可以看作是 IO 復(fù)用模型

AIO (Asynchronous I/O)AIO JDK 1.7 中引入 它是異步 IO 模型。 異步 IO 是基于事件和回調(diào)機(jī)制實(shí)現(xiàn)的,也就是應(yīng)用操作之后會(huì)直接返回,不會(huì)堵塞在那里,當(dāng)后臺(tái)處理完成,操作系統(tǒng)會(huì)通知相應(yīng)的線程進(jìn)行后續(xù)的操作。

3、Java的NIO應(yīng)用場(chǎng)景?相比于IO的優(yōu)勢(shì)在哪?

多路復(fù)用: NIO可以使用單個(gè)線程管理多個(gè)通道,這種多路復(fù)用的特性使得它非常適合處理大量的并發(fā)連接,例如網(wǎng)絡(luò)服務(wù)器。 NIO提供了選擇器(Selector)機(jī)制,可以通過(guò)一個(gè)線程管理多個(gè)通道的IO操作。當(dāng)某個(gè)通道有IO事件發(fā)生時(shí),可以通過(guò)選擇器進(jìn)行通知,從而實(shí)現(xiàn)高效的事件驅(qū)動(dòng)模型。

非阻塞 I/O NIO 支持非阻塞 I/O,這意味著在執(zhí)行 I/O 操作時(shí),線程不會(huì)被阻塞。這使得在網(wǎng)絡(luò)傳輸中可以有效地管理大量并發(fā)連接。

緩沖 NIO 讀寫(xiě)數(shù)據(jù)都是通過(guò)緩沖區(qū)進(jìn)行操作的。讀操作的時(shí)候?qū)?Channel 中的數(shù)據(jù)填充到 Buffer 中,而寫(xiě)操作時(shí)將 Buffer 中的數(shù)據(jù)寫(xiě)入到 Channel 中。NIO利用 Buffer 和Channel可以高效的管理網(wǎng)絡(luò)IO中的字節(jié)流數(shù)據(jù)。 這點(diǎn)類似于傳統(tǒng)IO中的 BufferedInputStream中的緩沖區(qū)。

總的來(lái)說(shuō): NIO性能優(yōu)勢(shì)主要體現(xiàn)在處理高并發(fā)的網(wǎng)絡(luò)IO場(chǎng)景。 傳統(tǒng) I/O 在網(wǎng)絡(luò)通信中主要使用阻塞式 I/O,為每個(gè)連接分配一個(gè)線程。當(dāng)連接數(shù)量增加時(shí),系統(tǒng)性能將受到嚴(yán)重影響,線程資源成為系統(tǒng)的性能瓶頸。而 NIO 提供了非阻塞 I/O 和 I/O 多路復(fù)用,可以在單個(gè)線程中處理多個(gè)并發(fā)連接,從而在網(wǎng)絡(luò)傳輸中顯著提高性能。

對(duì)于處理請(qǐng)求數(shù)較少或者少量連接讀寫(xiě)大文件的場(chǎng)景其優(yōu)勢(shì)相對(duì)于傳統(tǒng)IO并不明顯。

4、Java的IO、NIO、AIO 操作文件讀寫(xiě)

public static void main(String[] args) {

/*==========傳統(tǒng)IO==========*/

try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("123.txt"))

){

bufferedWriter.write("測(cè)試傳統(tǒng)IO");

} catch (IOException e) {

e.printStackTrace();

}

try (BufferedReader bufferedReader = new BufferedReader(new FileReader("123.txt"));){

String line;

while ((line = bufferedReader.readLine()) != null){

System.out.println(line);

}

} catch (Exception e) {

e.printStackTrace();

}

/*==========NIO==========*/

Path path = Paths.get("456.txt");

try ( FileChannel fileChannel = FileChannel.open(path, EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE))) {

ByteBuffer buffer = StandardCharsets.UTF_8.encode("測(cè)試NIO");

fileChannel.write(buffer);

} catch (IOException e) {

e.printStackTrace();

}

try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)){

ByteBuffer buffer = ByteBuffer.allocate(1024);

int bytesRead = fileChannel.read(buffer);

while (bytesRead != -1) {

buffer.flip();

System.out.println(StandardCharsets.UTF_8.decode(buffer));

buffer.clear();

bytesRead = fileChannel.read(buffer);

}

} catch (IOException e) {

e.printStackTrace();

}

/*==========AIO==========*/

// 使用 Paths.get() 獲取文件路徑

Path pathAIO = Paths.get("789.txt");

try {

AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(pathAIO, StandardOpenOption.WRITE, StandardOpenOption.CREATE);

ByteBuffer buffer = StandardCharsets.UTF_8.encode("測(cè)試AIO");

Future result = fileChannel.write(buffer, 0);

result.get();

fileChannel.close();

} catch (Exception e) {

e.printStackTrace();

}

try (AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(pathAIO, StandardOpenOption.READ)){

ByteBuffer buffer = ByteBuffer.allocate(1024);

// 異步讀取 主線程會(huì)繼續(xù)往下執(zhí)行 為了防止讀取完成之前 程序運(yùn)行結(jié)束 使用線程同步器來(lái)處理同步問(wèn)題

CountDownLatch countDownLatch = new CountDownLatch(1);

fileChannel.read(buffer, 0, buffer, new CompletionHandler<>() {

@Override

public void completed(Integer result, ByteBuffer attachment) {

attachment.flip();

System.out.println(StandardCharsets.UTF_8.decode(attachment));

attachment.clear();

countDownLatch.countDown();

}

@Override

public void failed(Throwable exc, ByteBuffer attachment) {

System.out.println("讀取失敗");

exc.printStackTrace();

countDownLatch.countDown();

}

});

// 等待異步操作完成

countDownLatch.await();

} catch (Exception e) {

e.printStackTrace();

}

}

5、NIO的核心類 :Buffer(緩沖區(qū))、Channel(通道)、Selector(選擇器)

在傳統(tǒng)的BIO(Blocking IO)中

IO 是面向流的處理,比如 InputStream和 OutputStream,面向流的 I/O 系統(tǒng)一次一個(gè)字節(jié)地處理數(shù)據(jù)。 NIO(Non-blocking IO) 是面向塊(緩沖區(qū))的處理,面向塊(緩沖區(qū))的 I/O 系統(tǒng)以緩存塊的形式處理數(shù)據(jù)。 有點(diǎn)類似于BIO中的 緩沖流 BufferedInputStream和BufferedOutputStream

在NIO體系中是以 Buffer 緩沖區(qū)和 Channel 通道配合來(lái)處理數(shù)據(jù)的。

Buffer(緩沖區(qū)): Buffer是抽象類: 其中最常用的之類是 ByteBuffer 字節(jié)緩沖區(qū)。

Buffer中維護(hù)了4個(gè)重要的變量用來(lái)描述緩沖區(qū)的功能特性。

capacity: 緩沖區(qū)能夠容納的數(shù)據(jù)元素的最大數(shù)量。容量在緩沖區(qū)創(chuàng)建時(shí)被設(shè)定,不能被改變,不能為負(fù)數(shù)。limit: 緩沖區(qū)的限制 是第一個(gè)不應(yīng)該讀取或?qū)懭氲脑氐乃饕?。緩沖區(qū)的限制不能為負(fù),并且不能大于其容量。 可以理解為Buffer 中可以讀/寫(xiě)數(shù)據(jù)的邊界。在寫(xiě)模式下,limit 代表最多能寫(xiě)入的數(shù)據(jù),一般等于 capacity(可以通過(guò)limit(int newLimit)方法設(shè)置);讀模式下,limit 等于 Buffer 中實(shí)際寫(xiě)入的數(shù)據(jù)大小。position: 下一個(gè)要被讀或?qū)懙脑氐乃饕?。position 會(huì)自動(dòng)由相應(yīng)的 get()和 put()函數(shù)更新。緩沖區(qū)的位置不能為負(fù),并且不能大于其限制。 從寫(xiě)操作模式到讀操作模式切換的時(shí)候(調(diào)用flip方法),position 都會(huì)歸零,這樣就可以從頭開(kāi)始讀寫(xiě)了。mark: 標(biāo)記,Buffer允許將位置直接定位到該標(biāo)記處,這是一個(gè)可選屬性;

JDK文檔中說(shuō)明:標(biāo)記、位置、限制和容量值遵守以下關(guān)系: 0 <= 標(biāo)記 <= 位置 <= 限制 <= 容量

**Channel ** Channel 通道只負(fù)責(zé)傳輸數(shù)據(jù)、不直接操作數(shù)據(jù)。操作數(shù)據(jù)都是通過(guò) Buffer 緩沖區(qū)來(lái)進(jìn)行操作!通常,通道可以分為兩大類:(FileChannel)文件通道和(SocketChannel)套接字通道。

BIO 中的流是單向的,分為各種 InputStream(輸入流)和 OutputStream(輸出流),數(shù)據(jù)只是在一個(gè)方向上傳輸(類似于通信信道的單工通信方式)。 通道與流的不同之處在于通道是雙向的,它可以用于讀、寫(xiě)或者同時(shí)用于讀寫(xiě)(類似于通信信道的全雙工通信方式)。

常用的Channel實(shí)現(xiàn):

FileChannel:用于文件 I/O 的通道,支持文件的讀、寫(xiě)和追加操作。FileChannel 允許在文件的任意位置進(jìn)行數(shù)據(jù)傳輸,支持文件鎖定以及內(nèi)存映射(涉及零拷貝優(yōu)化相關(guān)技術(shù))文件等高級(jí)功能。FileChannel 無(wú)法設(shè)置為非阻塞模式,因此它只適用于阻塞式文件操作。 SocketChannel:用于 TCP 套接字 I/O 的通道。SocketChannel 支持非阻塞模式,可以與 Selector一起使用,實(shí)現(xiàn)高效的網(wǎng)絡(luò)通信。SocketChannel 允許連接到遠(yuǎn)程主機(jī),進(jìn)行數(shù)據(jù)傳輸。 ServerSocketChannel:用于監(jiān)聽(tīng) TCP 套接字連接的通道。與 SocketChannel 類似,ServerSocketChannel 也支持非阻塞模式,并可以與 Selector 一起使用。ServerSocketChannel 負(fù)責(zé)監(jiān)聽(tīng)新的連接請(qǐng)求,接收到連接請(qǐng)求后,可以創(chuàng)建一個(gè)新的 SocketChannel 以處理數(shù)據(jù)傳輸。 DatagramChannel:用于 UDP 套接字 I/O 的通道。DatagramChannel 支持非阻塞模式,可以發(fā)送和接收數(shù)據(jù)報(bào)包,適用于無(wú)連接的、不可靠的網(wǎng)絡(luò)通信。 AsynchronousFileChannel:AsynchronousFileChannel 是 Java 7 引入的一個(gè)異步文件通道類,提供了對(duì)文件的異步讀、寫(xiě)、打開(kāi)和關(guān)閉等操作。

FileChannel的代碼示例:

public static void main(String[] args) {

try (FileChannel sourceChannel = FileChannel.open(Paths.get("C:\\123.txt"), StandardOpenOption.READ);

FileChannel destinationChannel = FileChannel.open(Paths.get("C:\\123_copy.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {

ByteBuffer buffer = ByteBuffer.allocate(8*1024);

// 如果還有數(shù)據(jù)就循環(huán)讀取寫(xiě)入

while (sourceChannel.read(buffer) != -1) {

// 轉(zhuǎn)換寫(xiě)模式

buffer.flip();

// 寫(xiě)入

destinationChannel.write(buffer);

// 寫(xiě)入后重置緩沖區(qū)

buffer.clear();

}

}catch (Exception e){

e.printStackTrace();

}

}

AsynchronousFileChannel的代碼示例

public static void main(String[] args){

Path path = Paths.get("123.txt");

// 使用 Future 方式

try (AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ)) {

ByteBuffer buffer = ByteBuffer.allocate(1024);

long position = 0;

while (true) {

Future result = fileChannel.read(buffer, position);

int bytesRead = result.get();

if (bytesRead <= 0) {

break;

}

position += bytesRead;

// 轉(zhuǎn)換成讀模式

buffer.flip();

byte[] data = new byte[buffer.limit()];

buffer.get(data);

System.out.println(new String(data));

buffer.clear();

}

}catch (Exception e){

e.printStackTrace();

}

// 實(shí)現(xiàn) CompletionHandler 接口方式

try (AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);){

ByteBuffer buffer = ByteBuffer.allocate(1024);

AtomicLong position = new AtomicLong(0);

CountDownLatch latch = new CountDownLatch(1);

fileChannel.read(buffer, position.get(), null, new CompletionHandler() {

@Override

public void completed(Integer bytesRead, Object attachment) {

if (bytesRead > 0) {

position.addAndGet(bytesRead);

buffer.flip();

byte[] data = new byte[buffer.limit()];

buffer.get(data);

System.out.print(new String(data));

buffer.clear();

fileChannel.read(buffer, position.get(), attachment, this);

} else {

latch.countDown();

}

}

@Override

public void failed(Throwable exc, Object attachment) {

System.out.println("Error: " + exc.getMessage());

latch.countDown();

}

});

latch.await();

} catch (Exception e) {

e.printStackTrace();

}

}

Selector Selector(選擇器) 是基于事件驅(qū)動(dòng)的 I/O 多路復(fù)用模型,它允許一個(gè)線程處理多個(gè) Channel(這點(diǎn)非常重要)。 Selector 模型通過(guò)將 I/O 操作轉(zhuǎn)化為事件驅(qū)動(dòng)的方式,實(shí)現(xiàn)了高效的多路復(fù)用,來(lái)提高系統(tǒng)的并發(fā)處理能力和效率。

Selector的重要特性:

事件注冊(cè): 在 Selector 模型中,程序會(huì)向 Selector 對(duì)象注冊(cè)感興趣的事件,這些事件可以是連接Socket建立、讀數(shù)據(jù)、寫(xiě)數(shù)據(jù)等。 也就是一個(gè)Selector 可以注冊(cè) 多個(gè)Channel,我們只需要使用一個(gè)線程管理Selector就能夠處理Selector 上的多個(gè)通道(Channel)。 NIO處理高并發(fā)的關(guān)鍵所在。 事件監(jiān)聽(tīng): Selector 會(huì)不斷地輪詢注冊(cè)在其上的通道(Channel),檢查這些通道是否有已經(jīng)就緒的事件發(fā)生。 事件處理: 當(dāng) Selector 發(fā)現(xiàn)某個(gè)通道上發(fā)生了感興趣的事件時(shí),它會(huì)通知程序,并且程序可以根據(jù)事件類型執(zhí)行相應(yīng)的操作。例如,如果一個(gè)通道的數(shù)據(jù)可讀,程序可以讀取數(shù)據(jù)并進(jìn)行處理;如果一個(gè)通道的數(shù)據(jù)可寫(xiě),程序可以將數(shù)據(jù)寫(xiě)入通道。 非阻塞式調(diào)用: 在 Selector 模型中,通常會(huì)使用非阻塞式調(diào)用(Non-blocking I/O),這意味著程序可以在等待事件發(fā)生時(shí)繼續(xù)執(zhí)行其他任務(wù),而不會(huì)被阻塞。 多路復(fù)用: Selector 能夠同時(shí)監(jiān)聽(tīng)多個(gè)通道,當(dāng)任何一個(gè)通道上發(fā)生感興趣的事件時(shí),Selector 都能及時(shí)地通知程序,因此能夠有效地實(shí)現(xiàn)多路復(fù)用,提高系統(tǒng)的并發(fā)處理能力。 SelectionKey抽象類表示 Channel通道 在 Selector 中的注冊(cè)信息以及與該通道相關(guān)聯(lián)的狀態(tài)和操作。

可以通過(guò)Selector 抽象類的 open方法 創(chuàng)建 Selector 實(shí)例:

try {

Selector selector = Selector.open();

} catch (IOException e) {

e.printStackTrace();

}

在一個(gè)Selector 實(shí)例中 有三種 SelectionKey 集合分別用來(lái)返回不同狀態(tài)的 Channel通道信息: 分別對(duì)應(yīng)Selector 中的三個(gè)方法:

keys方法: 返回的 Set 代表了注冊(cè)在該 Selector 上的 Channel selectedKeys方法: 返回的 Set 代表了所有可通過(guò) select() 方法獲取的、需要進(jìn)行 IO 處理的 Channel 已取消鍵集: 是已被取消但其通道尚未注銷的鍵的集合。不可直接訪問(wèn)此集合。在下一次執(zhí)行 select() 方法時(shí),這些 Channel 對(duì)應(yīng)的 SelectionKey 會(huì)被徹底刪除

SelectionKey 中定義了四種事件注冊(cè)類型:

public static final int OP_READ = 1;

public static final int OP_WRITE = 4;

public static final int OP_CONNECT = 8;

public static final int OP_ACCEPT = 16;

OP_READ(值為1):表示通道已經(jīng)準(zhǔn)備好進(jìn)行讀取操作。當(dāng)通道中有數(shù)據(jù)可讀時(shí),將觸發(fā)該事件。

OP_WRITE(值為4):表示通道已經(jīng)準(zhǔn)備好進(jìn)行寫(xiě)入操作。當(dāng)通道可寫(xiě)入數(shù)據(jù)時(shí),將觸發(fā)該事件。

OP_CONNECT(值為8):表示通道已經(jīng)完成連接操作。該事件通常在客戶端套接字進(jìn)行連接時(shí)使用。

OP_ACCEPT(值為16):表示服務(wù)器套接字已經(jīng)準(zhǔn)備好接受新的連接。該事件通常在服務(wù)器端套接字接受新連接時(shí)使用。

這些常量可以在 SelectionKey 的 interestOps() 方法中使用,用于指定注冊(cè)感興趣的事件類型。當(dāng)注冊(cè)的事件發(fā)生時(shí),將觸發(fā)相應(yīng)的操作。

Selector的簡(jiǎn)單代碼示例:

通過(guò) ServerSocketChannel 實(shí)現(xiàn)群聊功能 服務(wù)端

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;

public class TestA {

public static void main(String[] args) {

try {

// 創(chuàng)建ServerSocketChannel實(shí)例 接受客戶端連接請(qǐng)求

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

// 設(shè)置為非阻塞模式

// 當(dāng)調(diào)用 accept() 方法時(shí),如果沒(méi)有新的連接請(qǐng)求到達(dá),該方法將立即返回null,而不是阻塞等待新的連接。

// 這樣可以使服務(wù)器同時(shí)處理多個(gè)連接而不必為每個(gè)連接創(chuàng)建一個(gè)新的線程

serverSocketChannel.configureBlocking(false);

// 綁定 18848端口

serverSocketChannel.socket().bind(new InetSocketAddress(18848));

// 創(chuàng)建Selector實(shí)例

Selector selector = Selector.open();

// 注冊(cè)ServerSocketChannel 到 Selector ,監(jiān)聽(tīng) OP_ACCEPT 事件

serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

// 通過(guò)死循環(huán)持續(xù)監(jiān)聽(tīng) Selector 內(nèi)通道的事件

while (true) {

// 阻塞地監(jiān)聽(tīng)通道是否有事件發(fā)生。如果有通道已經(jīng)準(zhǔn)備好的事件,則 select() 方法會(huì)返回已經(jīng)就緒的通道數(shù)

int readyCounts = selector.select();

// 如果沒(méi)有就緒狀態(tài)的通道就繼續(xù)下一輪循環(huán)

if (readyCounts == 0) {

continue;

}

// 獲取 selectedKeys 需要進(jìn)行 IO 處理的 Channel

Set selectedKeys = selector.selectedKeys();

Iterator keyIterator = selectedKeys.iterator();

// 如果有就緒的通道 就循環(huán)處理

while (keyIterator.hasNext()) {

SelectionKey key = keyIterator.next();

if (key.isAcceptable()) {

// 處理連接事件

ServerSocketChannel server = (ServerSocketChannel) key.channel();

SocketChannel client = server.accept();

client.configureBlocking(false);

// 將客戶端通道注冊(cè)到 Selector 并監(jiān)聽(tīng) OP_READ 事件

client.register(selector, SelectionKey.OP_READ);

} else if (key.isReadable()) {

// 處理讀事件

SocketChannel client = (SocketChannel) key.channel();

ByteBuffer buffer = ByteBuffer.allocate(1024);

int bytesRead = client.read(buffer);

if (bytesRead > 0) {

buffer.flip();

System.out.println(new String(buffer.array(), 0, bytesRead));

// 將客戶端通道注冊(cè)到 Selector 并監(jiān)聽(tīng) OP_WRITE 事件

client.register(selector, SelectionKey.OP_WRITE);

} else if (bytesRead < 0) {

// 客戶端斷開(kāi)連接

client.close();

}

} else if (key.isWritable()) {

// 處理寫(xiě)事件

SocketChannel client = (SocketChannel) key.channel();

// 接收到客戶端的數(shù)據(jù)后 反饋給客戶端 發(fā)送成功

ByteBuffer buffer = ByteBuffer.wrap("發(fā)送成功".getBytes());

client.write(buffer);

// 將客戶端通道注冊(cè)到 Selector 并監(jiān)聽(tīng) OP_READ 事件

client.register(selector, SelectionKey.OP_READ);

}

keyIterator.remove();

}

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

客戶端

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.SocketChannel;

public class TestB {

private Selector selector;

private SocketChannel socketChannel;

private static final String HOST = "localhost";

private static final int PORT = 18848;

public TestB() {

try {

selector = Selector.open();

socketChannel = SocketChannel.open(new InetSocketAddress(HOST, PORT));

socketChannel.configureBlocking(false);

socketChannel.register(selector, SelectionKey.OP_READ);

System.out.println("連接到" + HOST + ":" + PORT + "群聊了");

} catch (IOException e) {

e.printStackTrace();

}

}

public void start() {

new Thread(() -> {

try {

while (true) {

if (selector.select() > 0) {

for (SelectionKey key : selector.selectedKeys()) {

selector.selectedKeys().remove(key);

if (key.isReadable()) {

readMessage();

}

}

}

}

} catch (IOException e) {

e.printStackTrace();

}

}).start();

try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {

sendMessage("我是TestB");

String input;

while ((input = reader.readLine()) != null) {

sendMessage("TestB:"+input);

}

} catch (IOException e) {

e.printStackTrace();

}

}

private void sendMessage(String message) throws IOException {

if (message != null && !message.trim().isEmpty()) {

ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());

socketChannel.write(buffer);

}

}

private void readMessage() throws IOException {

ByteBuffer buffer = ByteBuffer.allocate(1024);

int read = socketChannel.read(buffer);

if (read > 0) {

buffer.flip();

String msg = new String(buffer.array(), 0, read);

System.out.println(msg);

}

}

public static void main(String[] args) {

new TestB().start();

}

}

**java.nio.file.Files 類 ** Files 類是 Java NIO(New I/O)包的一部分,提供了對(duì)文件操作的高級(jí)支持,包括對(duì)文件通道、文件鎖等的操作。 相比于BIO的 File 類 支持更復(fù)雜和更高級(jí)的文件操作。 JDK1.7引入。

下面介紹一些常用的方法:

①、判斷文件是否存在

// Path 為路徑相關(guān)的接口抽象 ,

// LinkOption是一個(gè)枚舉類型,用于指定在處理文件時(shí)如何處理符號(hào)鏈接(symbolic links)。

//符號(hào)鏈接是指向另一個(gè)文件或目錄的特殊文件,類似于Unix系統(tǒng)中的軟鏈接或Windows系統(tǒng)中的快捷方式。

// LinkOption提供了兩個(gè)常量:

// NOFOLLOW_LINKS:表示在處理文件時(shí)不要跟蹤符號(hào)鏈接。如果指定了此選項(xiàng),在對(duì)文件進(jìn)行操作時(shí),將不會(huì)解析符號(hào)鏈接所指向的實(shí)際文件,而是直接操作符號(hào)鏈接本身。

// FOLLOW_LINKS:表示在處理文件時(shí)要跟蹤符號(hào)鏈接。如果指定了此選項(xiàng),在對(duì)文件進(jìn)行操作時(shí),會(huì)自動(dòng)解析符號(hào)鏈接,然后操作符號(hào)鏈接所指向的實(shí)際文件。

public static boolean exists(Path path, LinkOption... options)

public static void main(String[] args) {

Path path = Paths.get("D:\\123.txt");

// 默認(rèn) FOLLOW_LINKS

boolean exists = Files.exists(path);

System.out.println(exists);

}

②、創(chuàng)建文件

// FileAttribute是文件屬性或目錄屬性的抽象 常用實(shí)現(xiàn)有 PosixFileAttributes

// 例如可以使用 PosixFileAttributes 來(lái)創(chuàng)建具有特定權(quán)限的文件

public static Path createFile(Path path,

FileAttribute... attrs)

throws IOException

public static void main(String[] args) {

Path path = Paths.get("D:\\1234.txt");

try {

Files.createFile(path);

} catch (IOException e) {

e.printStackTrace();

}

}

③、創(chuàng)建目錄

public static void main(String[] args) {

Path path = Paths.get("D:\\12345");

try {

Files.createDirectory(path);

} catch (IOException e) {

e.printStackTrace();

}

}

④、刪除文件

public static void main(String[] args) {

Path path = Paths.get("D:\\1234.txt");

try {

Files.delete(path);

} catch (IOException e) {

e.printStackTrace();

}

}

⑤、復(fù)制文件

public static void main(String[] args) {

Path source = Paths.get("D:\\123.txt");

Path target = Paths.get("D:\\1234.txt");

try {

// StandardCopyOption 有三個(gè)屬性

// REPLACE_EXISTING:表示如果目標(biāo)文件已經(jīng)存在,則用源文件替換目標(biāo)文件。

//COPY_ATTRIBUTES:表示在復(fù)制文件時(shí)也復(fù)制文件的屬性。這些屬性包括文件的元數(shù)據(jù),例如文件權(quán)限、最后修改時(shí)間等。如果不使用此選項(xiàng),目標(biāo)文件將會(huì)獲得系統(tǒng)默認(rèn)的屬性。

//ATOMIC_MOVE:表示使用原子性操作來(lái)移動(dòng)文件。原子性操作是指在一個(gè)步驟內(nèi)完成的操作,要么全部成功,要么全部失敗,沒(méi)有中間狀態(tài)。這個(gè)選項(xiàng)通常用于將文件從一個(gè)位置原子性地移動(dòng)到另一個(gè)位置,確保移動(dòng)操作的完整性。

Files.copy(source,target, StandardCopyOption.REPLACE_EXISTING);

} catch (IOException e) {

e.printStackTrace();

}

}

⑥、移動(dòng)文件

public static void main(String[] args) {

Path source = Paths.get("D:\\123.txt");

Path target = Paths.get("D:\\1234.txt");

try {

Files.move(source,target, StandardCopyOption.REPLACE_EXISTING);

} catch (IOException e) {

e.printStackTrace();

}

}

⑦、讀取文本文件

public static void main(String[] args) {

Path path = Paths.get("D:\\1234.txt");

try {

List list = Files.readAllLines(path, StandardCharsets.UTF_8);

list.forEach(System.out::println);

} catch (IOException e) {

e.printStackTrace();

}

}

⑧、寫(xiě)入文本文件

public static void main(String[] args) {

Path path = Paths.get("D:\\1234.txt");

try {

// 追加模式

// Files.write(path,list,StandardCharsets.UTF_8, StandardOpenOption.APPEND);

List list = Arrays.asList("123", "456");

Files.write(path,list,StandardCharsets.UTF_8);

} catch (IOException e) {

e.printStackTrace();

}

}

⑨、遍歷

public class TestC {

public static void main(String[] args) {

Path path = Paths.get("D:\\");

try {

Files.walkFileTree(path,new MyFileVisitor());

} catch (IOException e) {

e.printStackTrace();

}

}

}

class MyFileVisitor extends SimpleFileVisitor {

@Override

public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {

// 準(zhǔn)備訪問(wèn)目錄

return super.preVisitDirectory(dir, attrs);

}

@Override

public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {

// 訪問(wèn)文件

System.out.println(file);

return super.visitFile(file, attrs);

}

@Override

public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {

// 訪問(wèn)文件失敗

return super.visitFileFailed(file, exc);

}

@Override

public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {

// 正在訪問(wèn)目錄

System.out.println(dir);

return super.postVisitDirectory(dir, exc);

}

}

⑩、利用重寫(xiě)visitFile方法查找文件

public class TestC {

public static void main(String[] args) {

// 查找D盤(pán)有沒(méi)有 123.txt 文件

Path path = Paths.get("D:\\");

try {

MyFileVisitor myFileVisitor = new MyFileVisitor("123.txt");

Files.walkFileTree(path,myFileVisitor);

System.out.println(myFileVisitor.searchFilePath);

} catch (IOException e) {

e.printStackTrace();

}

}

}

class MyFileVisitor extends SimpleFileVisitor {

private String searchFileName;

public Path searchFilePath;

public MyFileVisitor() {

}

public MyFileVisitor(String searchFileName) {

this.searchFileName = searchFileName;

}

@Override

public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {

String fileName = file.getFileName().toString();

if (fileName.equals(searchFileName)) {

searchFilePath = file;

// 如果找到了 就終止

return FileVisitResult.TERMINATE;

}

// 如果沒(méi)找到繼續(xù)查找

return FileVisitResult.CONTINUE;

}

}

6、Java NIO中的零拷貝優(yōu)化支持

零拷貝優(yōu)化: 推薦看幾篇博客大致了解下https://zhuanlan.zhihu.com/p/83398714 https://blog.csdn.net/a745233700/article/details/122660332?spm=1001.2014.3001.5506

零拷貝(Zero-copy)是一種計(jì)算機(jī)程序設(shè)計(jì)領(lǐng)域的優(yōu)化技術(shù),旨在減少數(shù)據(jù)在內(nèi)存之間復(fù)制的次數(shù),從而提升系統(tǒng)性能,特別是對(duì)于大量數(shù)據(jù)的傳輸尤為重要。在傳統(tǒng)的數(shù)據(jù)處理流程中,數(shù)據(jù)從一個(gè)位置(如磁盤(pán))讀取到操作系統(tǒng)內(nèi)核緩沖區(qū),再?gòu)膬?nèi)核緩沖區(qū)復(fù)制到用戶空間的應(yīng)用程序緩沖區(qū),然后在某些場(chǎng)景下(如網(wǎng)絡(luò)傳輸),數(shù)據(jù)可能還需要從用戶空間復(fù)制回內(nèi)核空間的網(wǎng)絡(luò)緩沖區(qū)以便發(fā)送。

零拷貝技術(shù)試圖消除或最小化這些不必要的數(shù)據(jù)復(fù)制步驟,具體方法有以下幾種:

用戶空間直接訪問(wèn)(User-Space Direct Access): 允許應(yīng)用程序直接訪問(wèn)內(nèi)核管理的內(nèi)存,例如通過(guò)內(nèi)存映射(mmap)文件,這樣數(shù)據(jù)可以從磁盤(pán)直接加載到用戶空間并用于網(wǎng)絡(luò)傳輸,無(wú)需先復(fù)制到內(nèi)核空間。 寫(xiě)時(shí)復(fù)制(Copy-on-Write): 在數(shù)據(jù)實(shí)際被修改前,多個(gè)進(jìn)程可以共享同一份數(shù)據(jù)的內(nèi)存映射,只有當(dāng)數(shù)據(jù)需要修改時(shí)才會(huì)創(chuàng)建數(shù)據(jù)的副本。 共享內(nèi)存(Shared Memory): 多個(gè)進(jìn)程可以直接訪問(wèn)同一塊內(nèi)存區(qū)域,避免數(shù)據(jù)在進(jìn)程間復(fù)制。 DMA(Direct Memory Access : 在硬件級(jí)別實(shí)現(xiàn)零拷貝,允許外圍設(shè)備(如網(wǎng)卡)直接讀寫(xiě)內(nèi)存,而不需要CPU介入數(shù)據(jù)搬運(yùn),常用于高速網(wǎng)絡(luò)傳輸。

通過(guò)這些技術(shù),零拷貝能夠顯著減少CPU使用率,降低內(nèi)存帶寬消耗,提升數(shù)據(jù)處理速度,尤其是在高負(fù)載的網(wǎng)絡(luò)服務(wù)器、數(shù)據(jù)庫(kù)和文件系統(tǒng)中效果明顯。

JDK中對(duì)于零拷貝的支持 MappedByteBuffer 和 FileChannel 的transferTo()/transferFrom()方法

MappedByteBuffer的使用 MappedByteBuffer 用于表示一個(gè)內(nèi)存映射文件,即將文件的一部分或全部映射到內(nèi)存中,以便通過(guò)直接操作內(nèi)存來(lái)實(shí)現(xiàn)對(duì)文件的讀寫(xiě)。這種方式可以提高文件 I/O 的性能,因?yàn)椴僮飨到y(tǒng)可以直接在內(nèi)存和磁盤(pán)之間傳輸數(shù)據(jù),無(wú)需通過(guò) Java 應(yīng)用程序進(jìn)行額外的數(shù)據(jù)拷貝。

MappedByteBuffer 是 NIO 基于內(nèi)存映射(mmap)這種零拷??式的提供的?種實(shí)現(xiàn),底層實(shí)際是調(diào)用了 Linux 內(nèi)核的 mmap 系統(tǒng)調(diào)用。它可以將一個(gè)文件或者文件的一部分映射到內(nèi)存中,形成一個(gè)虛擬內(nèi)存文件,這樣就可以直接操作內(nèi)存中的數(shù)據(jù),而不需要通過(guò)系統(tǒng)調(diào)用來(lái)讀寫(xiě)文件。

public static void main(String[] args) throws IOException {

try (FileChannel sourceChannel = FileChannel.open(Paths.get("D:\\123.txt"), StandardOpenOption.READ);

FileChannel destinationChannel = FileChannel.open(Paths.get("D:\\123.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.READ)) {

long fileSize = sourceChannel.size();

MappedByteBuffer sourceMappedBuffer = sourceChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize);

MappedByteBuffer destinationMappedBuffer = destinationChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);

for (int i = 0; i < fileSize; i++) {

// 一個(gè)字節(jié)一個(gè)字節(jié)寫(xiě)

byte b = sourceMappedBuffer.get(i);

destinationMappedBuffer.put(i, b);

}

}

}

transferTo()/transferFrom()方法 transferTo()/transferFrom()是 NIO 基于發(fā)送文件(sendfile)這種零拷貝方式的提供的一種實(shí)現(xiàn),底層實(shí)際是調(diào)用了 Linux 內(nèi)核的 sendfile系統(tǒng)調(diào)用。它可以直接將文件數(shù)據(jù)從磁盤(pán)發(fā)送到網(wǎng)絡(luò),而不需要經(jīng)過(guò)用戶空間的緩沖區(qū)。

public static void main(String[] args) {

Path sourcePath = Paths.get("123.txt");

Path destinationPath = Paths.get("123_copy.txt");

try (FileChannel sourceChannel = FileChannel.open(sourcePath, StandardOpenOption.READ);

FileChannel destinationChannel = FileChannel.open(destinationPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {

long position = 0;

long count = sourceChannel.size();

// 循環(huán)傳輸,直到所有字節(jié)都被傳輸

// 因?yàn)榫彌_區(qū)大小限制、通道的限制、網(wǎng)絡(luò)限制、文件系統(tǒng)限制、操作系統(tǒng)限制等因素可能會(huì)導(dǎo)致 transferTo 無(wú)法傳輸count 大小的數(shù)據(jù)

while (position < count) {

long transferred = sourceChannel.transferTo(position, count - position, destinationChannel);

position += transferred;

}

} catch (IOException e) {

e.printStackTrace();

}

}

柚子快報(bào)激活碼778899分享:Java的NIO體系

http://yzkb.51969.com/

推薦閱讀

評(píng)論可見(jiàn),查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。

轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/19106767.html

發(fā)布評(píng)論

您暫未設(shè)置收款碼

請(qǐng)?jiān)谥黝}配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問(wèn)

文章目錄