通道是NIO的一個主要創新,用於在Buffer與通道另外一端之間進行有效的數據傳輸,這點在NIO技術-1-緩衝區有講過,這裏不在贅述。java
I/O能夠分爲文件IO和流IO,那麼通道對應的就能夠分爲文件通道(FileChannel)和流通道(流通道就是套接字通道,SocketChannel),因此NIO中有四種通道實現類:數組
打開一個通道的方法以下:安全
//打開一個文件通道,指定爲可讀寫 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構造方法的第二個參數含義以下:
文件鎖在Jdk1.4時才被提供,當多個程序併發操做同一個文件時,可使用文件鎖來鎖定文件同一時刻只能接受一個程序的IO操做。文件鎖分爲獨佔鎖和共享鎖,FileChannel提供了文件鎖的API,在FileChannel的文件鎖方式中,鎖的對象是文件而不是通道或線程,也就是說文件鎖不適用於同一個JVM上多個線程併發訪問文件的狀況。同一個JVM中,一個線程得到了某個文件的獨佔鎖,第二個線程也能夠得到這個文件的獨佔鎖;可是,在不一樣的JVM中,第一個JVM的線程得到的某個文件的獨佔所,第二個JVM的線程會被阻塞。致使這樣的狀況的緣由是文件鎖是由操做系統在進程級上來判優的,而不是在線程級上。
文件鎖能夠經過FileChannel的lock()或tryLock()方法獲取,二者的區別以下:
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以下:
實際應用中,通常使用共享鎖讀文件,使用獨佔鎖寫文件