NIO--Channel
類結構圖
接口
Channel
public interface Channel extends Closeable {public boolean isOpen();public void close() throws IOException;}InterruptibleChannel
標記接口,表示支持中斷
AsynchronousChannel
標記接口,表示支持異步。
public interface AsynchronousChannel extends Channel {@Overridevoid close() throws IOException; }NetworkChannel
具有網絡功能。
public interface NetworkChannel extends Channel {NetworkChannel bind(SocketAddress local) throws IOException;SocketAddress getLocalAddress() throws IOException;<T> NetworkChannel setOption(SocketOption<T> name, T value) throws IOException;<T> T getOption(SocketOption<T> name) throws IOException;Set<SocketOption<?>> supportedOptions(); }MulticastChannel
具有多播功能
public interface MulticastChannel extends NetworkChannel {@Override void close() throws IOException;MembershipKey join(InetAddress group, NetworkInterface interf) throws IOException;MembershipKey join(InetAddress group, NetworkInterface interf, InetAddress source) throws IOException; }ReadableByteChannel
增加了read(...)方法。
public interface ReadableByteChannel extends Channel {public int read(ByteBuffer dst) throws IOException;}ScatteringByteChannel
支持從多個ByteBuffer中read數據。
public interface ScatteringByteChannel extends ReadableByteChannel {public long read(ByteBuffer[] dsts, int offset, int length) throws IOException;public long read(ByteBuffer[] dsts) throws IOException; }WritableByteChannel
增加了write(...)方法。
public interface WritableByteChannel extends Channel {public int write(ByteBuffer src) throws IOException; }GatheringByteChannel
支持把數據write到多個ByteBuffer中。
public interface GatheringByteChannelextends WritableByteChannel {public long write(ByteBuffer[] srcs, int offset, int length) throws IOException;public long write(ByteBuffer[] srcs) throws IOException;}ByteChannel
標記接口,表示具有read,write功能。
public interface ByteChannel extends ReadableByteChannel, WritableByteChannel {}SeekableByteChannel
具有定位方法。
public interface SeekableByteChannel extends ByteChannel {@Overrideint read(ByteBuffer dst) throws IOException;@Overrideint write(ByteBuffer src) throws IOException;//**********定位方法**********long position() throws IOException;SeekableByteChannel position(long newPosition) throws IOException;long size() throws IOException;SeekableByteChannel truncate(long size) throws IOException; }實現類
I/O可以分為廣義的兩大類別:File I/O和StreamI/O。那么相應地通道也有兩種類型,它們是文件(file)通道和套接字(socket)通道。套接字(socket)通道又分為:SocketChannel、ServerSocketChannel和DatagramChannel。
FileChannel
文件通道總是阻塞式的,因此不能被置于非阻塞模式。
FileChannel對象不能直接創建。一個FileChannel實例只能通過在一個打開的file對象(RandomAccessFile、FileInputStream或FileOutputStream)上調用getChannel()方法獲取。調用getChannel( )方法會返回一個連接到相同文件的FileChannel對象且該FileChannel對象具有與file對象相同的訪問權。
FileChannel對象是線程安全(thread-safe)的。多個進程可以在同一個實例上并發調用方法而不會引起任何問題,不過并非所有的操作都是多線程的(multithreaded)。影響通道位置或者影響文件大小的操作都是單線程的(single-threaded)。如果有一個線程已經在執行會影響通道位置或文件大小的操作,那么其他嘗試進行此類操作之一的線程必須等待。并發行為也會受到底層的操作系統或文件系統影響。
文件訪問
position值決定文件中哪一處的數據接下來將被讀或者寫。
將通道position設置為一個負值會導致java.lang.IllegalArgumentException異常
把position設置到超出文件尾,這樣做會把position設置為指定值而不改變文件大小。假如在將position設置為超出當前文件大小時實現了一個read()方法,那么會返回一個文件尾(end-of-file)條件;倘若此時實現的是一個write( )方法則會引起文件增長以容納寫入的字節,具體行為類似于實現一個絕對write( )并可能導致出現一個文件空洞(filehole)。
FileChannel位置(position)是從底層的文件描述符獲得的,該position同時被作為通道引用獲取來源的文件對象共享。這也就意味著一個對象對該position的更新可以被另一個對象看到。
帶position參數的絕對形式的read( )和write( )方法在返回值時不會改變當前的文件position。
truncate( )方法會砍掉您所指定的新size值之外的所有數據。文件的position會被設置為所提供的新size值。
force( )方法要求文件的所有待定修改立即同步到磁盤。
文件鎖定
鎖的對象是文件而不是通道或線程,這意味著文件鎖不適用于判優同一臺Java虛擬機上的多個線程發起的訪問。
如果一個線程在某個文件上獲得了一個獨占鎖,然后第二個線程利用一個單獨打開的通道來請求該文件的獨占鎖,那么第二個線程的請求會被批準。但如果這兩個線程運行在不同的Java虛擬機上,那么第二個線程會阻塞,因為鎖最終是由操作系統或文件系統來判優的并且幾乎總是在進程級而非線程級上判優。鎖都是與一個文件關聯的,而不是與單個的文件句柄或通道關聯。
public abstract FileLock lock(long position, long size, boolean shared) throws IOException; public final FileLock lock() throws IOException {return lock(0L, Long.MAX_VALUE, false); } public abstract FileLock tryLock(long position, long size, boolean shared) throws IOException;public final FileLock tryLock() throws IOException {return tryLock(0L, Long.MAX_VALUE, false); }鎖是在文件內部區域上獲得的。
要獲得一個共享鎖,必須先以只讀權限打開文件,而請求獨占鎖時則需要寫權限。
鎖定區域的范圍不一定要限制在文件的size值以內,鎖可以擴展從而超出文件尾。因此,可以提前把待寫入數據的區域鎖定,也可以鎖定一個不包含任何文件內容的區域,比如文件最后一個字節以外的區域。如果之后文件增長到達那塊區域,那么文件鎖就可以保護該區域的文件內容了。相反地,如果鎖定了文件的某一塊區域,然后文件增長超出了那塊區域,那么新增加的文件內容將不會受到文件鎖的保護。
tryLock( )的方法是lock( )方法的非阻塞變體。和lock( )方法起相同的作用,不過如果請求的鎖不能立即獲取到則會返回一個null。
FileLock
FileLock類封裝一個鎖定的文件區域。FileLock對象由FileChannel創建并且總是關聯到那個特定的通道實例。
public abstract class FileLock {public final FileChannel channel( ); public final long position( ); public final long size( ) ;public final boolean isShared( ) ;public final boolean overlaps (long position, long size) ;public abstract boolean isValid( ); public abstract void release( ) throws IOException; }channel( )方法來查詢一個lock對象以判斷它是由哪個通道創建的
一個FileLock對象創建之后即有效,直到它的release()方法被調用或它所關聯的通道被關閉或Java虛擬機關閉時才會失效。我們可以通過調用isValid()布爾方法來測試一個鎖的有效性。一個鎖的有效性可能會隨著時間而改變,不過它的其他屬性——位置(position)、范圍大小(size)和獨占性(exclusivity)——在創建時即被確定,不會隨著時間而改變。
isShared()方法來測試一個鎖以判斷它是共享的還是獨占的。如果底層的操作系統或文件系統不支持共享鎖,那么該方法將總是返回false值。
overlaps( )方法來查詢一個FileLock對象是否與一個指定的文件區域重疊。這將使您可以迅速判斷您擁有的鎖是否與一個感興趣的區域(region of interest)有交叉
使用完一個鎖后而不釋放它的話,可能會導致沖突或者死鎖
內存映射文件
public abstract MappedByteBuffer map(MapMode mode,long position, long size) throws IOException;map()的方法,該方法可以在一個打開的文件和一個特殊類型的ByteBuffer之間建立一個虛擬內存映射。它會創建一個由磁盤文件支持的虛擬內存映射(virtual memory mapping)并在那塊虛擬內存空間外部封裝一個MappedByteBuffer對象
MappedByteBuffer對象的行為在多數方面類似一個基于內存的緩沖區,只不過該對象的數據元素存儲在磁盤上的一個文件中。調用get()方法會從磁盤文件中獲取數據,此數據反映該文件的當前內容。相似地,對映射的緩沖區實現一個put()會更新磁盤上的那個文件,并且您做的修改對于該文件的其他閱讀者也是可見的。
通過內存映射機制來訪問一個文件會比使用常規方法讀寫高效得多,甚至比使用通道的效率都高。因為不需要做明確的系統調用,那會很消耗時間。更重要的是,操作系統的虛擬內存可以自動緩存內存頁(memory page)。這些頁是用系統內存來緩存的,所以不會消耗Java虛擬機內存堆(memory heap)。
與文件鎖的范圍機制不一樣,映射文件的范圍不應超過文件的實際大小。如果請求一個超出文件大小的映射,文件會被增大以匹配映射的大小。即使您請求的是一個只讀映射,map( )方法也會嘗試這樣做。
映射模式:
- READ_ONLY
- READ_WRITE
- PRIVATE:寫時拷貝(copy-on-write)的映射
寫時拷貝(copy-on-write)的映射。這意味著您通過put()方法所做的任何修改都會導致產生一個私有的數據拷貝并且該拷貝中的數據只有MappedByteBuffer實例可以看到。該過程不會對底層文件做任何修改,而且一旦緩沖區被施以垃圾收集動作(garbagecollected),那些修改都會丟失。盡管寫時拷貝的映射可以防止底層文件被修改,您也必須以read/write權限來打開文件以建立MapMode.PRIVATE映射。只有這樣,返回的MappedByteBuffer對象才能允許使用put( )方法。
映射緩沖區沒有綁定到創建它們的通道上。關閉相關聯的FileChannel不會破壞映射,只有丟棄緩沖區對象本身才會破壞該映射。
Channel-to-Channel傳輸
由于經常需要從一個位置將文件數據批量傳輸到另一個位置,FileChannel類添加了一些優化方法來提高該傳輸過程的效率:
public abstract long transferFrom(ReadableByteChannel src, long position, long count)throws IOException; public abstract long transferTo(long position, long count, WritableByteChannel target)throws IOException;transferTo( )和transferFrom( )方法允許將一個通道交叉連接到另一個通道,而不需要通過一個中間緩沖區來傳遞數據。只有FileChannel類有這兩個方法,因此channel-to-channel傳輸中通道之一必須是FileChannel。
直接的通道傳輸不會更新與某個FileChannel關聯的position值。請求的數據傳輸將從position參數指定的位置開始,傳輸的字節數不超過count參數的值。實際傳輸的字節數會由方法返回,可能少于您請求的字節數。
示例:
private static void catFiles (WritableByteChannel target, String [] files) throws Exception
{
for (int i = 0; i < files.length; i++)
{
???? FileInputStream fis = new FileInputStream (files [i]);
???? FileChannel channel = fis.getChannel( );
???? channel.transferTo (0, channel.size( ), target);
???? channel.close( );
???? fis.close( );
}
}
Socket通道
socket通道類可以運行非阻塞模式并且是可選擇的.
DatagramChannel和SocketChannel實現定義讀和寫功能的接口而ServerSocketChannel不實現。ServerSocketChannel負責監聽傳入的連接和創建新的SocketChannel對象,它本身從不傳輸數據。
全部socket通道類(DatagramChannel、SocketChannel和ServerSocketChannel)在被實例化時都會創建一個對等socket對象。
非阻塞模式
Socket通道可以在非阻塞模式下運行。傳統Java socket是阻塞的。要把一個socket通道置于非阻塞模式,我們要依靠所有socket通道類的公有超級類:SelectableChannel。
public abstract class SelectableChannel extends AbstractInterruptibleChannel implements Channel {......public abstract SelectableChannel configureBlocking(boolean block) throws IOException;public abstract boolean isBlocking();public abstract Object blockingLock();}configureBlocking()方法設置或重新設置一個通道的阻塞模式,參數值為true則設為阻塞模式,參數值為false值設為非阻塞模式。
isBlocking( )方法來判斷某個socket通道當前處于哪種模式
SocketChannel sc = SocketChannel.open( );
sc.configureBlocking (false);
// nonblocking ...
if ( ! sc.isBlocking( ))
{ doSomething (cs); }
blockingLock()方法,防止socket通道的阻塞模式被更改。該方法會返回一個非透明的對象引用。返回的對象是通道實現修改阻塞模式時內部使用的。只有擁有此對象的鎖的線程才能更改通道的阻塞模式(對象的鎖是用同步的Java密碼獲取的,它不同于lock( )方法)
Socket socket = null;
Object lockObj = serverChannel.blockingLock( );
// have a handle to the lock object, but haven't locked it yet
// may block here until lock is acquired
synchronize (lockObj) {
???????? // This thread now owns the lock; mode can't be changed
???????? boolean prevState = serverChannel.isBlocking( );
???????? serverChannel.configureBlocking (false);
??????? socket = serverChannel.accept( );
??????? serverChannel.configureBlocking (prevState);
?}
?// lock is now released, mode is allowed to change
?if (socket != null)
?{
?doSomethingWithTheSocket (socket);
?}
ServerSocketChannel
ServerSocketChannel是一個基于通道的socket監聽器。同我java.net.ServerSocket執行相同的基本任務,不過它增加了通道語義,因此能夠在非阻塞模式下運行。
用靜態的open()工廠方法創建一個新的ServerSocketChannel對象,將會返回同一個未綁定的java.net.ServerSocket關聯的通道。該對等ServerSocket可以通過在返回的ServerSocketChannel上調用socket( )方法來獲取。
?ServerSocketChannel ssc = ServerSocketChannel.open( );
?ServerSocket serverSocket = ssc.socket( );
?serverSocket.bind (new InetSocketAddress (1234));
accept( )方法在阻塞模式下,總是阻塞并返回一個java.net.Socket對象,返回的對象能夠在非阻塞模式下運行
如果以非阻塞模式被調用,當沒有傳入連接在等待時,ServerSocketChannel.accept( )會立即返回null
?ServerSocketChannel ssc = ServerSocketChannel.open( );
?ServerSocket serverSocket = ssc.socket( );
?serverSocket.bind (new InetSocketAddress (1234));
?
?ByteBuffer buffer = ByteBuffer.wrap (GREETING.getBytes( ));
?ServerSocketChannel ssc = ServerSocketChannel.open( );
?
?ssc.socket( ).bind (new InetSocketAddress (port));
//非阻塞模式
?ssc.configureBlocking (false);
?while (true)
?{
???? ...
???? SocketChannel sc = ssc.accept( );
???? if (sc == null) {
???? ...
???? } else {
???? ...
???? }
?}
SocketChannel
open( )方法可以創建一個新的SocketChannel對象,而在新創建的SocketChannel上調用socket( )方法能返回它對等的Socket對象;在該Socket上調用getChannel( )方法則能返回最初的那個SocketChannel。
新創建的SocketChannel雖已打開卻是未連接的??梢酝ㄟ^在通道上直接調用connect( )方法或在通道關聯的Socket對象上調用connect( )來將該socket通道連接。isConnected( )方法來測試某個SocketChannel當前是否已連接
調用finishConnect( )方法來完成連接過程,該方法任何時候都可以安全地進行調用。假如在一個非阻塞模式的SocketChannel對象上調用finishConnect( )方法,將可能出現下列情形之一:
- ?connect( )方法尚未被調用。那么將產生NoConnectionPendingException異常。
- 連接建立過程正在進行,尚未完成。那么什么都不會發生,finishConnect( )方法會立即返回false值。
- 在非阻塞模式下調用connect( )方法之后,SocketChannel又被切換回了阻塞模式。那么如果有必要的話,調用線程會阻塞直到連接建立完成,finishConnect( )方法接著就會返回true值。
- 在初次調用connect( )或最后一次調用finishConnect( )之后,連接建立過程已經完成。那么SocketChannel對象的內部狀態將被更新到已連接狀態,finishConnect( )方法會返回true值,然后SocketChannel對象就可以被用來傳輸數據了。
- 連接已經建立。那么什么都不會發生,finishConnect( )方法會返回true值。
Socket通道是線程安全的。并發訪問時無需特別措施來保護發起訪問的多個線程,不過任何時候都只有一個讀操作和一個寫操作在進行中。
connect( )和finishConnect( )方法是互相同步的,并且只要其中一個操作正在進行,任何讀或寫的方法調用都會阻塞,即使是在非阻塞模式下
DatagramChannel
SocketChannel模擬連接導向的流協議(如TCP/IP),DatagramChannel則模擬包導向的無連接協議(如UDP/IP)
DatagramChannel對象既可以充當服務器(監聽者)也可以充當客戶端(發送者)
DatagramChannel是無連接的
管道
管道就是一個用來在兩個實體之間單向傳輸數據的導管
public abstract class Pipe {protected Pipe() { }public abstract SourceChannel source();public abstract SinkChannel sink();public static Pipe open() throws IOException {return SelectorProvider.provider().openPipe();} }Pipe類創建一對提供環回機制的Channel對象。這兩個通道的遠端是連接起來的,以便任何寫在SinkChannel對象上的數據都能出現在SourceChannel對象。唯一可保證的是寫到SinkChannel中的字節都能按照同樣的順序在SourceChannel上重現。
Pipe pipe = Pipe.open( );
WritableByteChannel w = pipe.sink( );
ReadableByteChannel r = pipe.source( );
w.write (buffer);
.......
r.read (buffer);
通道工具類
java.nio.channels.Channels
| 方法 | 返回 | 描述 |
| newChannel (InputStream in) | ?ReadableByteChannel | 返回一個將從給定的輸入流讀取數據的通道。 |
| newChannel (OutputStream out)? | WritableByteChannel | 返回一個將向給定的輸出流寫入數據的通道。 |
| newInputStream (ReadableByteChannel ch) | ?InputStream? | 返回一個將從給定的通道讀取字節的流。 |
| newOutputStream (WritableByteChannel ch) | ?OutputStream | 返回一個將向給定的通道寫入字節的流。 |
| newReader (ReadableByteChannel ch, CharsetDecoder dec, int minBufferCap) | ?Reader? | 返回一個reader,它將從給定的通道讀取字節并依據提供的CharsetDecoder對讀取到的字節進行解碼 |
| newReader(ReadableByteChannel ch, String csName) | ?Reader? | 返回一個reader,它將從給定的通道讀取字節并依據提供的字符集名稱將讀取到的字節解碼成字符。 |
| newWriter (WritableByteChannel ch, CharsetEncoder dec, int minBufferCap) | ?Writer | 返回一個writer,它將使用提供的CharsetEncoder對象對字符編碼并寫到給定的通道中。 |
| newWriter (WritableByteChannel ch, String csName) | ?Writer? | 返回一個writer,它將依據提供的字符集名稱對字符編碼并寫到給定的通道中。 |
?
總結
以上是生活随笔為你收集整理的NIO--Channel的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring--IoC(2)
- 下一篇: NIO--Selector