NIO技術-2-通道概論&文件通道

通道是NIO的一個主要創新,用於在Buffer與通道另外一端之間進行有效的數據傳輸,這點在NIO技術-1-緩衝區有講過,這裏不在贅述。java

 I/O能夠分爲文件IO和流IO,那麼通道對應的就能夠分爲文件通道(FileChannel)和流通道(流通道就是套接字通道,SocketChannel),因此NIO中有四種通道實現類:數組

  1. FileChannel:文件通道,用於操做文件I/O
  2. ServerSocketChannel:服務器套接字通道,用於TCP鏈接響應客戶端鏈接
  3. SocketChannel:套接字通道,用於TCP協議,客戶端鏈接服務器後,服務器和客戶端都會有一個
  4. SocketChannel,就能夠互相發送數據了
  5. DatagramChannel:數據報通道,用於UDP協議

打開一個通道的方法以下:安全

//打開一個文件通道,指定爲可讀寫
RandomAccessFile raf = new RandomAccessFile("d:/test.txt", "rw");
FileChannel fc = raf.getChannel();
// 打開一個服務器套接字通道
ServerSocketChannel ssc = ServerSocketChannel.open();
// 打開一個套接字通道,
SocketChannel sc = SocketChannel.open();
// 打開一個數據報通道
DatagramChannel dc = DatagramChannel.open();

文件通道還能夠經過底層文件句柄的的方式得到,可是這樣有可能致使不能讀寫文件服務器

//不要使用這種方式獲取通道實例
FileInputStream fis = new FileInputStream("d:/test.txt");
FileChannel fileChannel = fis.getChannel();
ByteBuffer buff = ByteBuffer.allocate(8192);
fileChannel.write(buff);

 還能夠經過java.nio.channels.Channels這個工具類獲取通道實例,下面是一個例子:網絡

// 建立一個可讀通道  
ReadableByteChannel rbc = Channels.newChannel(System.in);  
// 建立一個可寫通道  
WritableByteChannel wbc = Channels.newChannel(System.out);  
// 建立一個大小爲8192字節的字節緩衝區  
ByteBuffer buff = ByteBuffer.allocate(8192);  
// 輪詢將可讀通道的數據讀到緩衝區  
while (rbc.read(buff) != -1) {  
    // 翻轉緩衝區  
    buff.flip();  
    String str = new String(buff.array()).trim();  
    // 若輸入"bye"則關閉通道  
    if (str.equals("bye")) {  
        rbc.close();  
        wbc.close();  
        break;  
    }  
    // 將緩衝區的數據寫入到可寫通道  
    wbc.write(buff);  
    // 輪詢緩衝區是否還有剩餘數據  
    while (buff.hasRemaining()) {  
        wbc.write(buff);  
    }  
    // 清空緩衝區  
    buff.clear();  
}

通道能夠以阻塞(blocking)或非阻塞(non-blocking)模式運行,阻塞模式會一直等待某個操做直到返回結果;非阻塞不會一直等待,要麼返回null,要麼返回執行完的結果。只有流通道才能已non-blocking模式運行,如Socket和Pipe。併發

Socket通道類繼承SelectableChannel,只有SelectableChannel類才能與選擇器(Selector)一塊兒使用。dom

關閉通道使用close()方法,調用close()方法根據操做系統的網絡實現不一樣可能會出現阻塞,能夠在任什麼時候候屢次調用close();若出現阻塞,第一次調用close()後會一直等待;若第一次調用close()成功關閉後,以後再調用close()會當即返回,不會執行任何操做。工具

在一個已關閉的通道上進行I/O操做會拋出ClosedChannelException,能夠通道isOpen()方法來檢查通道時候爲打開狀態。操作系統

 NIO中的通道都實現了InterruptibleChannel,若某個線程上有一個處於阻塞狀態的通道,線程被中斷會拋出ClosedByInterruptException,並會關閉通道。能夠調用isInterrupted()方法檢查某個線程的interrupt狀態。線程

1、分散讀到多個緩衝區 & 從多個緩衝區彙集寫入通道

//test.txt中只有一行數據:whoareu?  
//建立2個緩衝區,組成一個緩衝區數組  
//打開一個文件通道,將test.txt中的數據讀到緩衝區數組中  
//緩衝區數組會自動填充buffA和buffB這兩個緩衝區  
//buffA填滿後,再繼續填充buffB  
//達到分散讀取數據到多個緩衝區中  
ByteBuffer buffA = ByteBuffer.allocate(6);  
ByteBuffer buffB = ByteBuffer.allocate(5);  
ByteBuffer[] buffArr = { buffA, buffB };  
RandomAccessFile raf = new RandomAccessFile("test.txt", "r");  
FileChannel fc = raf.getChannel();  
fc.read(buffArr);  
System.out.println(new String(buffA.array()));//輸出who  
System.out.println(new String(buffB.array()));//輸出areu?  
fc.close();  
raf.close();
//建立兩個Buffer,組成一個Buffer Array  
//將Buffer Array的數據寫入到文件通道到  
//達到彙集緩衝區其中寫入通道目的  
byte[] byteA = "hello ".getBytes("UTF-8");  
ByteBuffer buffC = ByteBuffer.wrap(byteA);  
byte[] byteB = "world!".getBytes("UTF-8");  
ByteBuffer buffD = ByteBuffer.wrap(byteB);  
ByteBuffer[] allBuff = {buffC,buffD};  
RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");  
FileChannel fc = raf.getChannel();  
fc.write(allBuff);  
fc.close();  
raf.close();

2、文件通道(FileChannel)

FileChannel不能直接建立,只能經過建立一個文件對象(RandAccessFile、FileInputStream、FileOutputStream)後調用其getChannel()方法得到。FileChannel是線程安全,多個進程併發操做同一文件不會引發任何問題;兵法行爲受低層操做系統或文件系統影響。

FileChannel類保證同一個JVM上的全部FileChannel實例看到的文件內容是一致的,但不能保證外部的非Java進程看到的該文件視圖一致,也可能一致,這取決於低層操做系統的實現。

打開一個文件通道可使用下面方式:

//RandomAccessFile有2中構造方法,下面的構造方法等同於:  
//RandomAccessFile raf = new RandomAccessFile(new File("test.txt"), "rw");  
RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");  
FileChannel fc = raf.getChannel();

RandomAccessFile構造方法的第二個參數含義以下:

  1. "rw":對文件可讀可寫,若文件不存在則會建立該文件
  2. "r":只讀
  3. "rws":對文件可讀寫,而且文件中數據和元信息(若更新時間等)的每一個更新都會寫入到磁盤
  4. "rwd":對文件可讀寫,而且文件數據的每一個更新會同步寫入到磁盤

文件鎖在Jdk1.4時才被提供,當多個程序併發操做同一個文件時,可使用文件鎖來鎖定文件同一時刻只能接受一個程序的IO操做。文件鎖分爲獨佔鎖和共享鎖,FileChannel提供了文件鎖的API,在FileChannel的文件鎖方式中,鎖的對象是文件而不是通道或線程,也就是說文件鎖不適用於同一個JVM上多個線程併發訪問文件的狀況。同一個JVM中,一個線程得到了某個文件的獨佔鎖,第二個線程也能夠得到這個文件的獨佔鎖;可是,在不一樣的JVM中,第一個JVM的線程得到的某個文件的獨佔所,第二個JVM的線程會被阻塞。致使這樣的狀況的緣由是文件鎖是由操做系統在進程級上來判優的,而不是在線程級上。

文件鎖能夠經過FileChannel的lock()或tryLock()方法獲取,二者的區別以下:

  1. lock():阻塞,第一個線程獲取文件鎖後,第二個線程必須等待
  2. tryLock():非阻塞,若不能當即得到文件鎖則返回null
RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");  
FileChannel fc = raf.getChannel();  
//--------------------------------------------  
//阻塞得到文件獨佔鎖,並鎖定文件全部數據  
FileLock lock0 = fc.lock();  
//阻塞得到文件獨佔鎖,並鎖定文件指定數據  
FileLock lock1 = fc.lock(0, 8192, false);  
//阻塞得到文件共享鎖,並鎖定文件0 ~ 8192字節的數據  
FileLock lock2 = fc.lock(0, 8192, true);  
//--------------------------------------------  
//非阻塞獲取文件獨佔所,等同於lock()  
FileLock lock3 = fc.tryLock();  
//非阻塞獲取文件獨佔所,等同於fc.lock(0, 8192, false);  
FileLock lock4 = fc.tryLock(0, 8192, false);  
//非阻塞獲取文件共享鎖,等同於fc.lock(0, 8192, true);  
FileLock lock5 = fc.tryLock(0, 8192, true);

FileLock對象關聯FileChannel,FileLock API以下:

  1. channel():獲取關聯的FileChannel
  2. isShared():判斷是共享鎖仍是獨佔鎖,返回ture是共享鎖,返回false是獨佔鎖
  3. overlaps(long position, long size):判斷當前文件鎖鎖定的區域是否有交叉,也就是是否也被別線程鎖定了
  4. isValid():判斷當前文件鎖是否有效
  5. release():釋放文件鎖,通道被關閉或JVM關閉時也會釋放文件鎖。

實際應用中,通常使用共享鎖讀文件,使用獨佔鎖寫文件

相關文章
相關標籤/搜索