Java NIO核心概念總結(jié)篇

一把曼、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);
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末雄嚣,一起剝皮案震驚了整個濱河市晒屎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缓升,老刑警劉巖鼓鲁,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異港谊,居然都是意外死亡骇吭,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門歧寺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來燥狰,“玉大人,你說我怎么就攤上這事斜筐×拢” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵顷链,是天一觀的道長目代。 經(jīng)常有香客問我,道長嗤练,這世上最難降的妖魔是什么榛了? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮煞抬,結(jié)果婚禮上霜大,老公的妹妹穿的比我還像新娘。我一直安慰自己革答,他們只是感情好战坤,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布遮婶。 她就那樣靜靜地躺著,像睡著了一般湖笨。 火紅的嫁衣襯著肌膚如雪旗扑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天慈省,我揣著相機與錄音臀防,去河邊找鬼。 笑死边败,一個胖子當著我的面吹牛袱衷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播笑窜,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼致燥,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了排截?” 一聲冷哼從身側(cè)響起嫌蚤,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎断傲,沒想到半個月后脱吱,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡认罩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年箱蝠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垦垂。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡宦搬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出劫拗,到底是詐尸還是另有隱情间校,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布杨幼,位于F島的核電站撇簿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏差购。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一汉嗽、第九天 我趴在偏房一處隱蔽的房頂上張望欲逃。 院中可真熱鬧,春花似錦饼暑、人聲如沸稳析。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杯缺。三九已至,卻和暖如春匾鸥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工蜕劝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留暑始,地道東北人唉俗。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓股缸,卻偏偏與公主長得像坎背,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容