Java是很是繁雜的語言, 好比IO就是典型的表明...java
首先1.0, 是基於8位字節流的InputStream和OutputStream系列
而後是1.1, 是基於16位的字符流(unicode)的Reader和Writer系列緩存
下表是對應關係, 其中InputStreamReader和OutputStreamWriter, 起到兩個系列之間的適配做用安全
固然實際的繼承關係比這個複雜的多, 經過裝飾模式, 產生各類IO類, 很是繁雜數據結構
從1.4開始, Java提供new IO
其實在new IO中主要兩個提高
a, buffer和channel
解決小塊數據傳輸帶來的效率問題, 引入buffer數據結構, 能夠批量傳輸以提升效率(這樣更符合底層數據傳輸的方式), 一個挖煤的比喻, 從挖一鏟運一鏟到挖滿一卡車再運出來 多線程
b, 對於socket channel加入非阻塞方式併發
提供一種支持豐富操做的數據結構, 同時雖然對於全部的類型(除bool類型)都有相應的buffer, 可是隻有ByteBuffer能夠直接被channel讀取, 其他的都須要在放到channel以前作類型轉換dom
數據結構socket
支持操做性能
下圖以FileChannel爲例子,
首先Channel只能接收ByteBuffer做爲write/read的參數
對於其餘的buffer必須作類型轉換, 尤爲對於CharBuffer須要考慮charset的encode/decodethis
管道很形象, 就是鏈接發送端和接收端之間的媒介
其實就是對於傳統socket或file接口針對ByteBuffer的封裝
I/O能夠分爲廣義的兩大類別:File I/O和Stream I/O
因此對應的, Channel分爲兩類, FileChannel和Socket相關channel(SocketChannel、ServerSocketChannel和 DatagramChannel)
FileChannel
FileChannel類能夠實現經常使用的read,write以及scatter/gather操做(對於多個buffer的批處理), 同時它也提供了不少專用於文件的新方法.
文件通道老是阻塞式的, 由於現代操做系統都有複雜的緩存和預取機制, 因此本地磁盤I/O操做延遲不多
FileChannel對象不能直接建立。一個FileChannel實例只能經過在一個打開的file對象(RandomAccessFile、FileInputStream或 FileOutputStream)上調用getChannel( )方法獲取
FileChannel對象是線程安全(thread-safe)
1.4中, Java經過FileChannel實現了文件鎖(以前Java不支持文件鎖), 可是這是進程級別鎖, 相同進程中不一樣線程沒法經過文件鎖進行互斥
SocketChannel
新的socket通道類能夠運行非阻塞模式, 這個大大提高了Java IO的性能, 以前只能使用多線程阻塞的方式來處理併發, 但線程調度的開銷也很高尤爲當維護大量線程的時候
所有socket通道類(DatagramChannel、SocketChannel和ServerSocketChannel), 分別對應於java.net中的(Socket、ServerSocket和DatagramSocket), 而且Channel其實就是對他們的封裝
ServerSocketChannel
ByteBuffer buffer = ByteBuffer.wrap (GREETING.getBytes( )); ServerSocketChannel ssc = ServerSocketChannel.open( ); ssc.socket( ).bind (new InetSocketAddress (port)); ssc.configureBlocking (false); //non-blocking while (true) { System.out.println ("Waiting for connections"); SocketChannel sc = ssc.accept( ); //不會blocking直接返回 if (sc == null) { // no connections, snooze a while Thread.sleep (2000); } else { System.out.println ("Incoming connection from: " + sc.socket().getRemoteSocketAddress( )); buffer.rewind( ); sc.write (buffer); sc.close( ); } }
SocketChannel
InetSocketAddress addr = new InetSocketAddress (host, port); SocketChannel sc = SocketChannel.open( ); sc.configureBlocking (false); //設置non-blocking sc.connect (addr); //不會阻塞等待 while ( ! sc.finishConnect( )) { doSomethingElse( ); } doSomethingWithChannel (sc); sc.close( );
從上面兩個例子能夠看出channel的使用, 其實和原來的API沒有很大的不一樣, 關鍵就是支持non-blocking方式
固然還須要selector, 否則非阻塞意義不大, 象C/C++中的select, poll
Selector selector = Selector.open( );
channel1.register (selector, SelectionKey.OP_READ);
channel2.register (selector, SelectionKey.OP_WRITE);
channel3.register (selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
// Wait up to 10 seconds for a channel to become ready
readyCount = selector.select (10000);
只有繼承SelectableChannel的Channel類才能夠被註冊到Selector對象上, 因此FileChannel對象不是可選擇的, 而全部SocketChannel都是可選擇的
SelectionKey
表明了Selector和SelectableChannel的註冊關係
key.attachment(); //返回SelectionKey的attachment,attachment能夠在註冊channel的時候指定
key.channel(); // 返回該SelectionKey對應的channel
key.selector(); // 返回該SelectionKey對應的Selector
key.interestOps(); //返回表明須要Selector監控的IO操做的bit mask
key.readyOps(); //返回一個bit mask,表明在相應channel上能夠進行的IO操做
package java.nio.channels; //ops, selector所關心的通道操做,讀(read),寫(write),鏈接(connect)和接受(accept) public abstract SelectionKey register (Selector sel, int ops) throws ClosedChannelException; public abstract class SelectionKey { public static final int OP_READ; public static final int OP_WRITE; public static final int OP_CONNECT; public static final int OP_ACCEPT; public abstract SelectableChannel channel( ); public abstract Selector selector( ); public abstract void cancel( ); public abstract boolean isValid( ); public abstract int interestOps( ); public abstract void interestOps (int ops); public abstract int readyOps( ); public final boolean isReadable( ); public final boolean isWritable( ); public final boolean isConnectable( ); public final boolean isAcceptable( ); public final Object attach (Object ob) public final Object attachment( ) }
使用的代碼
// Allocate an unbound server socket channel ServerSocketChannel serverChannel = ServerSocketChannel.open(); // Get the associated ServerSocket to bind it with ServerSocket serverSocket = serverChannel.socket(); // Create a new Selector for use below Selector selector = Selector.open(); // Set the port the server channel will listen to serverSocket.bind(new InetSocketAddress(port)); // Set nonblocking mode for the listening socket serverChannel.configureBlocking(false); // Register the ServerSocketChannel with the Selector serverChannel.register(selector, SelectionKey.OP_ACCEPT); //關注accept while (true) { // This may block for a long time. Upon returning, the // selected set contains keys of the ready channels. int n = selector.select(); if (n == 0) { continue; // nothing to do }
// Get an iterator over the set of selected keys Iterator it = selector.selectedKeys().iterator(); // Look at each key in the selected set while (it.hasNext()) { SelectionKey key = (SelectionKey) it.next(); // Is a new connection coming in? if (key.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel channel = server.accept(); registerChannel(selector, channel, SelectionKey.OP_READ); //Accept後設置成關注Read sayHello(channel); } // Is there data to read on this channel? if (key.isReadable()) { readDataFromSocket(key); } } } }