一把曼、Java NIO基本介紹
Java NIO(New IO杨帽,也有人叫:Non Blocking IO)是從Java1.4版本開始引入的一個新的IO API,其與原來的IO有同樣的作用和目的嗤军,但是使用方式有很大的差別注盈。NIO是為提供I/O吞吐量而專門設(shè)計,其卓越的性能甚至可以與C媲美叙赚。NIO是通過Reactor模式的事件驅(qū)動機制來達到Non blocking的老客,那么什么是Reactor模式呢僚饭?Reactor翻譯成中文是“反應(yīng)器”,就是我們將事件注冊到Reactor中胧砰,當有相應(yīng)的事件發(fā)生時鳍鸵,Reactor便會告知我們有哪些事件發(fā)生了,我們再根據(jù)具體的事件去做相應(yīng)的處理尉间。
NIO支持面向緩沖區(qū)的偿乖、基于通道的IO操作,將以更加高效的方式進行文件的讀寫操作哲嘲。
NIO的三個核心模塊:Buffer(緩沖區(qū))贪薪、Channel(通道)、Selector(選擇器)眠副。
二画切、Java NIO與IO的主要區(qū)別
IO NIO
面向流(Stream Oriented) 面向緩沖區(qū)(Buffer Oriented)
阻塞IO(Blocking IO) 非阻塞IO(Non Blocking IO)
無 選擇器(Selectors)
三、通道和緩沖區(qū)
1.緩沖區(qū):
1.1 基本概念:
緩沖區(qū)(Buffer)就是在內(nèi)存中預(yù)留指定字節(jié)數(shù)的存儲空間用來對輸入/輸出(I/O)的數(shù)據(jù)作臨時存儲囱怕,這部分預(yù)留的內(nèi)存空間就叫做緩沖區(qū)霍弹;
1.2 作用:
用來臨時存儲數(shù)據(jù),可以理解為是I/O操作中數(shù)據(jù)的中轉(zhuǎn)站娃弓。緩沖區(qū)直接為通道(Channel)服務(wù)庞萍,寫入數(shù)據(jù)到通道或從通道讀取數(shù)據(jù),這樣的操利用緩沖區(qū)數(shù)據(jù)來傳遞就可以達到對數(shù)據(jù)高效處理的目的忘闻。
1.3 類型:
Buffer就像一個數(shù)組钝计,可以保存多個相同類型的數(shù)據(jù),根據(jù)數(shù)據(jù)類型的不同(Boolean類型除外)齐佳,有以下七個Buffer常用的子類:ByteBuffer私恬、CharBuffer 、ShortBuffer 炼吴、IntBuffer 本鸣、LongBuffer 、FloatBuffer 硅蹦、DoubleBuffer 荣德。
1.4 緩沖區(qū)的四個基本屬性
屬性 | 概念 |
---|---|
容量(capacity) | 表示Buffer最大的數(shù)據(jù)容量,緩沖區(qū)的容量不能為負數(shù)童芹,而且一旦創(chuàng)建涮瞻,不可修改 |
限制(limit) | 緩沖區(qū)中當前的數(shù)據(jù)量,即位于limit之后的數(shù)據(jù)不可讀寫 |
位置(position) | 下一個要讀取或?qū)懭氲臄?shù)據(jù)的索引 |
標記(mark) | 調(diào)用mark()方法來記錄一個特定的位置:mark=position假褪,然后再調(diào)用reset()可以讓position恢復(fù)到標記的位置即position=mark |
容量署咽、限制、位置、標記遵守以下不變式:0 <= mark <= position <= limit <= capacity
1.5 創(chuàng)建緩沖區(qū):
獲取一個指定容量的xxxBuffer對象宁否,以ByteBuffer為例:
創(chuàng)建一個容量大小為1024的ByteBuffer數(shù)組窒升,需要注意的是:所有的緩沖區(qū)類都不能直接使用new關(guān)鍵字實例化,它們都是抽象類慕匠,但是它們都有一個用于創(chuàng)建相應(yīng)實例的靜態(tài)工廠方法:static XxxBuffer allocate(int capacity);
1.6 緩沖區(qū)的兩個數(shù)據(jù)操作方法:
put()和get()
get();獲取Buffer中的數(shù)據(jù)put()饱须,放入數(shù)據(jù)到Buffer中。
1.7 flip()方法:
將寫數(shù)據(jù)狀態(tài)切換為讀數(shù)據(jù)狀態(tài)
flip()的源碼:
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
具體操作代碼如下:
// 1.創(chuàng)建一個容量為1024的緩沖區(qū)
ByteBuffer buf = ByteBuffer.allocate(1024);
// 2.往緩沖區(qū)里寫數(shù)據(jù)
String str = "abcde";
buf.put(str.getBytes());
// 3.切換數(shù)據(jù)模式
buf.flip();
// 4.讀取數(shù)據(jù)
byte[] dst = new byte[buf.limit()];
buf.get(dst);
1.8 直接緩沖區(qū)與非直接緩沖區(qū)
(1)非直接緩沖區(qū)
首先看看非直接緩沖區(qū)台谊。我們之前說過NIO通過通道連接磁盤文件與應(yīng)用程序蓉媳,通過緩沖區(qū)存取數(shù)據(jù)進行雙向的數(shù)據(jù)傳輸。物理磁盤的存取是操作系統(tǒng)進行管理的青伤,與物理磁盤中的數(shù)據(jù)操作需要經(jīng)過內(nèi)核地址空間督怜;而我們的Java應(yīng)用程序是通過JVM分配的緩沖空間殴瘦。有點雷同于一個屬于核心態(tài)狠角,一個屬于應(yīng)用態(tài)的意思,而數(shù)據(jù)需要在內(nèi)核地址空間和用戶地址空間蚪腋,在操作系統(tǒng)和JVM之間進行數(shù)據(jù)的來回拷貝丰歌,無形中增加的中間環(huán)節(jié)使得效率與后面要提的之間緩沖區(qū)相比偏低。
(2)直接緩沖區(qū)
直接緩沖區(qū)則不再通過內(nèi)核地址空間和用戶地址空間的緩存數(shù)據(jù)的復(fù)制傳遞屉凯,而是在物理內(nèi)存中申請了一塊空間立帖,這塊空間映射到內(nèi)核地址空間和用戶地址空間,應(yīng)用程序與磁盤之間的數(shù)據(jù)存取之間通過這塊直接申請的物理內(nèi)存進行悠砚。
(3)直接與非直接緩沖區(qū)的要點
字節(jié)緩沖區(qū)要么是直接的晓勇,要么是非直接的。如果為直接字節(jié)緩沖區(qū)灌旧,則 Java 虛擬機會盡最大努力直接在此緩沖區(qū)上執(zhí)行本機 I/O 操作绑咱。也就是說,在每次調(diào)用操作系統(tǒng)基礎(chǔ)的一個本機 I/O 操作之前(或之后)枢泰,虛擬機都會盡量避免將緩沖區(qū)的內(nèi)容復(fù)制到中間緩沖區(qū)中(或從中間緩沖區(qū)中復(fù)制內(nèi)容)描融。
直接字節(jié)緩沖區(qū)可以通過調(diào)用此類的 allocateDirect() 工廠方法來創(chuàng)建。此方法返回的緩沖區(qū)進行分配和取消分配所需成本通常高于非直接緩沖區(qū)衡蚂。直接緩沖區(qū)的內(nèi)容可以駐留在常規(guī)的垃圾回收堆之外窿克,因此,它們對應(yīng)用程序的內(nèi)存需求量造成的影響可能并不明顯毛甲。所以年叮,建議將直接緩沖區(qū)主要分配給那些易受基礎(chǔ)系統(tǒng)的本機 I/O 操作影響的大型、持久的緩沖區(qū)玻募。一般情況下谋右,最好僅在 直接緩沖區(qū)能在程序性能方面帶來明顯好處時 分配它們。
直接字節(jié)緩沖區(qū)還可以通過 FileChannel 的 map() 方法 將文件區(qū)域直接映射到內(nèi)存中來創(chuàng)建补箍。該方法返回MappedByteBuffer 改执。 Java 平臺的實現(xiàn)有助于通過 JNI 從本機代碼創(chuàng)建直接字節(jié)緩沖區(qū)啸蜜。如果以上這些緩沖區(qū)中的某個緩沖區(qū)實例指的是不可訪問的內(nèi)存區(qū)域,則試圖訪問該區(qū)域不會更改該緩沖區(qū)的內(nèi)容辈挂,并且將會在訪問期間或稍后的某個時間導(dǎo)致拋出不確定的異常衬横。
字節(jié)緩沖區(qū)是直接緩沖區(qū)還是非直接緩沖區(qū)可通過調(diào)用其 isDirect() 方法來確定。提供此方法是為了能夠在性能關(guān)鍵型代碼中執(zhí)行顯式緩沖區(qū)管理
那么既然直接緩沖區(qū)的性能更高终蒂、效率更快蜂林,為什么還要存在兩種緩沖區(qū)呢?因為直接緩沖區(qū)也存在著一些缺點:
1)不安全
2)消耗更多拇泣,因為它不是在JVM中直接開辟空間噪叙。這部分內(nèi)存的回收只能依賴于垃圾回收機制,垃圾什么時候回收不受我們控制霉翔。
3)數(shù)據(jù)寫入物理內(nèi)存緩沖區(qū)中睁蕾,程序就喪失了對這些數(shù)據(jù)的管理,即什么時候這些數(shù)據(jù)被最終寫入從磁盤只能由操作系統(tǒng)來決定债朵,應(yīng)用程序無法再干涉子眶。
(4)選擇方法
直接緩沖區(qū)適合與數(shù)據(jù)長時間存在于內(nèi)存,或者大數(shù)據(jù)量的操作時更加適合序芦。
2.通道:
2.1 基本概念:
表示IO源與目標(例如:文件臭杰、套接字)打開的連接。但是通道(Channel)本身不能直接訪問數(shù)據(jù)谚中,需要與緩沖區(qū)(Buffer)配合才能實現(xiàn)數(shù)據(jù)的讀取操作渴杆。
2.2 作用:
如果把緩沖區(qū)理解為火車,那么通道就是鐵路宪塔,即通道(Channel)負責傳輸磁奖,緩存區(qū)(Buffer)負責存儲。
2.3 Channel的主要實現(xiàn)類:
實現(xiàn)類 | 概念 |
---|---|
FileChannel | 用于本地讀取蝌麸、寫入点寥、映射和操作文件的通道 |
DatagramChannel | 通過UDP讀寫網(wǎng)絡(luò)中的數(shù)據(jù)通道 |
SocketChannel | 通過TCP讀寫網(wǎng)絡(luò)中的數(shù)據(jù)通道 |
ServerSocketChannel | 可以監(jiān)聽新進來的TCP連接,對每一個新進來的連接都會創(chuàng)建一個SocketChannel |
2.4 支持通道的類:
用于本地IO操作:FileInputStream来吩、FileOutputStream敢辩、RandomAccessFile(隨機文件存儲流)
用于網(wǎng)絡(luò)IO操作:DatagramSocket、Socket弟疆、ServerSocket
2.5 獲取通道的三種方法
方式一:getChannel()方法
FileInputStream fis = new FileInputStream("NIO.pdf");
FileOutputStream fos = new FileOutputStream("newNIO.pdf");
// 獲取通道
FileChannel inChannel = fis.getChannel();
FileChannel outChannel = fos.getChannel();
方式二:通過Files類的靜態(tài)方法newByteChannel()獲取
FileChannel inChannel = null;
FileChannel outChannel = null;
// 獲取通道
inChannel = (FileChannel) Files.newByteChannel(Paths.get("NIO.pdf"), StandardOpenOption.READ);
outChannel = (FileChannel) Files.newByteChannel(Paths.get("newNIO.pdf"),
StandardOpenOption.READ, StandardOpenOption.WRITE,StandardOpenOption.CREATE);
方式三:通過通道的靜態(tài)方法:open()獲取
FileChannel inChannel = null;
FileChannel outChannel = null;
// 獲取通道
inChannel = FileChannel.open(Paths.get("NIO.pdf"), StandardOpenOption.READ);
outChannel = FileChannel.open(Paths.get("newNIO.pdf"), StandardOpenOption.READ,
StandardOpenOption.WRITE, StandardOpenOption.CREATE);
2.6 通道中的數(shù)據(jù)傳輸
(1) 將Buffer中的數(shù)據(jù)寫入Channel中:
int bytesWritten = inChannel.write(buf);
(2)從Channel讀取數(shù)據(jù)到Buffer中:
int bytesRead = inChannel.read(buf);
四戚长、分散和聚集
分散讀取(Scatter)指從Channel中讀取的數(shù)據(jù)“分散”到多個Buffer中怠苔。按照緩沖區(qū)的順序露氮,從Channel中讀取的數(shù)據(jù)依次將Buffer填滿胎许。
聚集寫入(Gather)指將多個Buffer中的數(shù)據(jù)“聚集”到Channel中挂脑。按照緩沖區(qū)的順序,寫入position和limit之間的數(shù)據(jù)到Channel中去锅劝。
transferFrom和transferTo用法:
transferFrom用法:
RandomAccessFile fromFile = new RandomAccessFile("fromNIO.pdf", "rw");
// 獲取FileChannel
FileChannel fromChannel = fromFile.getChannel();
RandomAccessFile toFile = new RandomAccessFile("toNIO.pdf", "rw");
FileChannel toChannel = toFile.getChannel();
// 定義傳輸位置
long position = oL;
// 最多傳輸?shù)淖止?jié)數(shù)
long count = fromChannel.size();
// 將數(shù)據(jù)從源通道傳輸?shù)搅硪粋€通道
toChannel.transferFrom(fromChannel, count, position);
transferTo()用法:和transferFrom相比,position和fromChannel這兩個參數(shù)的位置換了
RandomAccessFile fromFile = new RandomAccessFile("fromNIO.pdf", "rw");
// 獲取FileChannel
FileChannel fromChannel = fromFile.getChannel();
RandomAccessFile toFile = new RandomAccessFile("toNIO.pdf", "rw");
FileChannel toChannel = toFile.getChannel();
// 定義傳輸位置
long position = oL;
// 最多傳輸?shù)淖止?jié)數(shù)
long count = fromChannel.size();
// 將數(shù)據(jù)從源通道傳輸?shù)搅硪粋€通道
toChannel.transferFrom(position, count, fromChannel);
五蟆湖、阻塞與非阻塞
阻塞:
基本概念:傳統(tǒng)的IO流都是阻塞式的。也就是說隅津,當一個線程調(diào)用read()或者write()方法時诬垂,該線程將被阻塞伦仍,直到有一些數(shù)據(jù)讀讀取或者被寫入结窘,在此期間,該線程不能執(zhí)行其他任何任務(wù)。在完成網(wǎng)絡(luò)通信進行IO操作時斤讥,由于線程會阻塞,所以服務(wù)器端必須為每個客戶端都提供一個獨立的線程進行處理鉴竭,當服務(wù)器端需要處理大量的客戶端時责静,性能急劇下降。
非阻塞:
基本概念:Java NIO是非阻塞式的垃喊。當線程從某通道進行讀寫數(shù)據(jù)時乌助,若沒有數(shù)據(jù)可用時志笼,該線程會去執(zhí)行其他任務(wù)把篓。線程通常將非阻塞IO的空閑時間用于在其他通道上執(zhí)行IO操作纫溃,所以單獨的線程可以管理多個輸入和輸出通道。因此NIO可以讓服務(wù)器端使用一個或有限幾個線程來同時處理連接到服務(wù)器端的所有客戶端韧掩。
下面給出一個例子:分別用阻塞和非阻塞的方式實現(xiàn)紊浩,內(nèi)容中涉及到的選擇器(Selector)會在后面進行講解。
阻塞式:
// NIO的阻塞
public class TestNIOBlockDemo1 {
// 客戶端
@Test
public void client() throws IOException{
SocketChannel socketChannel = null;
FileChannel inChannel = null;
// 1 創(chuàng)建一個socket連接
socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
// 2 獲取通道
inChannel = FileChannel.open(Paths.get("file/1.txt"), StandardOpenOption.READ);
// 3 分配指定大小的緩沖區(qū)
ByteBuffer buf = ByteBuffer.allocate(1024);
// 4 讀取本地文件疗锐,并發(fā)送到服務(wù)端
while(inChannel.read(buf) != -1){ // 不等于-1坊谁,就說明讀到了東西
buf.flip(); // 將讀模式轉(zhuǎn)換為寫模式
socketChannel.write(buf);
buf.clear(); // 清空緩存區(qū)
}
// 5 通知服務(wù)端,客戶端已經(jīng)傳輸完畢
socketChannel.shutdownOutput();
// 6 接收服務(wù)端的反饋信息
int len = 0;
while((len = socketChannel.read(buf)) != -1){
buf.flip();
System.out.println(new String(buf.array(), 0, len));
buf.clear();
}
//7 關(guān)閉通道
socketChannel.close();
inChannel.close();
}
// 服務(wù)端
@Test
public void server() throws IOException{
// 1 獲取通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
FileChannel outChannel = FileChannel.open(Paths.get("file/2.txt"), StandardOpenOption.READ, StandardOpenOption.CREATE);
// 2 綁定連接
serverSocketChannel.bind(new InetSocketAddress(9898));
// 3 獲取客戶端的連接通道
SocketChannel socketChannel = serverSocketChannel.accept();
// 4 分配指定大小的緩沖區(qū)
ByteBuffer buf = ByteBuffer.allocate(1024);
// 5 接收客戶端的數(shù)據(jù)窒悔,并保存到本地
while(socketChannel.read(buf) != -1){
buf.flip();
outChannel.write(buf);
buf.clear();
}
// 6 接收完成呜袁,發(fā)送反饋給客戶端
buf.put("服務(wù)端接收數(shù)據(jù)成功!".getBytes());
buf.flip();
socketChannel.write(buf);
// 7 關(guān)閉通道
socketChannel.close();
outChannel.close();
serverSocketChannel.close();
}
}
非阻塞式:
// 非阻塞NIO
public class TestNonNIOBlockDemo1 {
// 客戶端
@Test
public void client() throws IOException{
// 1.獲取通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
// 2.切換非阻塞模式
socketChannel.configureBlocking(false);
// 3.分配指定大小的緩沖區(qū)
ByteBuffer buf = ByteBuffer.allocate(1024);
// 4. 發(fā)送數(shù)據(jù)給服務(wù)端
Scanner scan = new Scanner(System.in);
while(scan.hasNext()){
String str = scan.next();
buf.put((new Date().toString() + "\n" + str).getBytes());
buf.flip();
socketChannel.write(buf);
buf.clear();
}
// 5.關(guān)閉通道
socketChannel.close();
}
// 服務(wù)端
@Test
public void server() throws IOException{
// 1.獲取通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 2.切換非阻塞式模式
serverSocketChannel.configureBlocking(false);
// 3.綁定連接
serverSocketChannel.bind(new InetSocketAddress(9898));
// 4.獲取選擇器
Selector selector = Selector.open();
// 5.將通道注冊到選擇器上简珠,并指定“監(jiān)聽接收事件”
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// 6.輪詢式的獲取選擇器上已經(jīng)“準備就緒”的事件
while(selector.select() > 0){
// 7.獲取當前選擇器中所有注冊的“選擇鍵(已經(jīng)就緒的監(jiān)聽事件)”
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while(it.hasNext()){
// 8.獲取準備“就緒”的事件
SelectionKey sk = it.next();
// 9.判斷具體是什么事件準備就緒,不同的事件有不同的處理方法
if(sk.isAcceptable()){
// 10.若是“接收就緒”阶界,獲取客戶端連接狀態(tài)通道
SocketChannel socketChannel = serverSocketChannel.accept();
// 11.切換非阻塞式模式
socketChannel.configureBlocking(false);
// 12.將該通道注冊到選擇器上
socketChannel.register(selector, SelectionKey.OP_READ);
}
else if(sk.isReadable()){
// 13.獲取當前選擇器上“讀就緒”狀態(tài)的通道
SocketChannel socketChannel = (SocketChannel) sk.channel();
// 14.讀取數(shù)據(jù)
ByteBuffer buf = ByteBuffer.allocate(1024);
int len = 0;
while((len = socketChannel.read(buf)) > 0){
buf.flip();
System.out.println(new String(buf.array(), 0, len));
buf.clear();
}
}
// 15.取消選擇鍵SelectionKey
it.remove();
}
}
}
}
ps:從上面的例子也可以看出來使用NIO完成網(wǎng)絡(luò)通信的三個核心為:
1虹钮、通道(Channel):負責連接
2、緩沖區(qū)(Buffer):負責數(shù)據(jù)的存儲
3膘融、選擇器(Selector):監(jiān)控狀態(tài)
六芙粱、選擇器(Selector)
1、基本概念
選擇器(Selector):是可選擇通道(SelectableChannel)對象的多路復(fù)用器氧映,Selector可以同時監(jiān)控多個SelectableChannel的IO狀況春畔,提供了詢問通道是否已經(jīng)準備好執(zhí)行每個I/O操作的能力,即利用Selector可以使一個單獨的線程管理多個Channel岛都,這樣會大量的減少線程之間上下文切換的開銷律姨。
可選擇通道(SelectableChannel):SelectableChannel這個抽象類提供了實現(xiàn)通道的可選擇性所需要的公共方法。它是所有支持就緒檢查的通道類的父類臼疫。因為FileChannel類沒有繼承SelectableChannel因此是不是可選通道择份,而所有socket通道都是可選擇的,包括從管道(Pipe)對象的中獲得的通道烫堤。SelectableChannel可以被注冊到Selector對象上荣赶,同時可以指定對那個選擇器而言,那種操作是感興趣的鸽斟。一個通道可以被注冊到多個選擇器上拔创,但對每個選擇器而言只能被注冊一次。
選擇器鍵(SelectionKey):表示SelectableChannel和Selector之間的注冊關(guān)系富蓄。每次像選擇器注冊通道時就會選擇一個事件(選擇鍵)剩燥。選擇鍵包含兩個數(shù)值的操作集,指示了該注冊關(guān)系所關(guān)心的通道操作格粪,以及通道已經(jīng)準備好的操作躏吊。
使用Selector管理多個channel的結(jié)構(gòu)圖
2氛改、Selector的使用
1帐萎、創(chuàng)建選擇器(Selector)
Selector selector = Selector.open();
2、將Channel注冊到Selector
// Channel必須處于非阻塞模式下
channel.configureBlocking(false);
// 將Channel注冊到Selector中
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
register參數(shù)說明:
第一個參數(shù):selector代表注冊到哪個選擇器中胜卤;
第二個參數(shù):是“interest集合”疆导,表示選擇器所關(guān)心的通道操作,它實際上是一個表示選擇器在檢查通道就緒狀態(tài)時需要關(guān)心的操作的比特掩碼葛躏。比如一個選擇器對通道的read和write操作感興趣澈段,那么選擇器在檢查該通道時,只會檢查通道的read和write操作是否已經(jīng)處在就緒狀態(tài)舰攒。它有以下四種可以監(jiān)聽的事件類型:
事件類型 表示方法
讀 SelectionKey.OP_READ
寫 SelectionKey.OP_WRITE
連接 SelectionKey.OP_CONNECT
接收 SelectionKey.OP_ACCEPT
若注冊時不止監(jiān)聽一個事件败富,則可以使用“位或”操作符連接:
int interestSet = SelectionKey.OP_READ|SelectionKey.OP_WRITE
當通道觸發(fā)了某個操作之后,表示該通道的某個操作已經(jīng)就緒摩窃,可以被操作兽叮。因此:
某個SocketChannel成功連接到另一個服務(wù)器稱為“連接就緒”(OP_CONNECT)芬骄;
一個ServerSocketChannel準備好接收新進入的連接稱為“接收就緒”(OP_ACCEPT);
一個有數(shù)據(jù)可讀的通道可以說是“讀就緒”(OP_READ)鹦聪;
等待寫數(shù)據(jù)的通道可以說是“寫就緒”(OP_WRITE)账阻。
3、取消選擇鍵SelectionKey
remove();方法
七泽本、管道(Pipe)
管道是2個線程之間的單向數(shù)據(jù)連接淘太。Pipe有一個source通道和一個sink通道。數(shù)據(jù)會從source通道讀取规丽,并且寫入到sink通道蒲牧。
public class PipeTest {
@Test
public void test() throws IOException{
String str = "pcwl_java";
// 創(chuàng)建管道
Pipe pipe = Pipe.open();
// 向管道中寫入數(shù)據(jù),需要訪問sink通道 赌莺,SinkChannel是內(nèi)部類
Pipe.SinkChannel sinkChannel = pipe.sink();
// 通過SinkChannel的write()方法寫數(shù)據(jù)
ByteBuffer buf1 = ByteBuffer.allocate(1024);
buf1.clear();
buf1.put(str.getBytes());
buf1.flip();
while(buf1.hasRemaining()){
sinkChannel.write(buf1);
}
// 從管道中讀取數(shù)據(jù)造成,需要訪問source通道,SourceChannel是內(nèi)部類
Pipe.SourceChannel sourceChannel = pipe.source();
// 調(diào)用source通道的read()方法來讀取數(shù)據(jù)
ByteBuffer buf2 = ByteBuffer.allocate(1024);
sourceChannel.read(buf2);
}
}