Java7 I/O新功能探秘:同步操作,多播與隨機存取

fmms 12年前發布 | 19K 次閱讀 Java Java開發

Java7 I/O新功能探秘:同步操作,多播與隨機存取

  • SeekableByteChannel:隨機訪問通道;
  • MulticastChannel:允許IP多播的通道;
  • NetworkChannel:新的網絡通道超級接口;
  • 異步I/O API:新的API使I/O操作可以異步進行。

SeekableByteChannel

首先,Java 7包括新的ByteChannel - SeekableByteChannel,這個通道維護當前的位置,你可以讀寫該位置,并允許隨機訪問。使用這個類型的通道,你可以添加多個線程讀/寫在不同位置相同的線程。
SeekableByteChannel channel1 = Paths.get("Path to file").newByteChannel(); //Simply READ
SeekableByteChannel channel2 = Paths.get("Path to file").newByteChannel(StandardOpenOption.READ, StandardOpenOption.WRITE); //READ and WRITE
你可以使用下面這些方法操作位置和通道的大小。
  • long position():返回目前的位置;
  • long size():返回通道連接實體的當前大小,如通道連接的文件大小;
  • position(long newPosition):移動當前位置到某個地方;
  • truncate(long size):根據給定大小截斷實體。
position()和truncate()方法簡單地返回當前通道,允許鏈式調用。現在FileChannel實現了新的接口,使用所有FileChannel你都可以實現隨機訪問,當然你可以用它讀取一個文件:
SeekableByteChannel channel = null;
try {
    channel = Paths.get("Path to file").newByteChannel(StandardOpenOption.READ);
    ByteBuffer buffer = ByteBuffer.allocate(4096);

System.out.println("File size: " + channel.size());

while (channel.read(buffer) > 0) {
    buffer.rewind();

    System.out.print(new String(buffer.array(), 0, buffer.remaining()));

    buffer.flip();

    System.out.println("Current position : " + channel.position());
}

} catch (IOException e) { System.out.println("Expection when reading : " + e.getMessage()); e.printStackTrace(); } finally { if (sbc != null){ channel.close(); } }</pre>

MulticastChannel

這個新的接口允許開啟IP多播,因此你可以向一個完整的組發送和接收IP數據報。多播實現了直接綁定本地多播設備,這個接口是通過 DatagramChannel和AsynchronousDatagramChannel實現的。下面是從Javadoc中摘取的一個打開 DatagramChannel t的簡單示例:
NetworkInterface networkInterface = NetworkInterface.getByName("hme0");

DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET) .setOption(StandardSocketOption.SO_REUSEADDR, true) .bind(new InetSocketAddress(5000)) .setOption(StandardSocketOption.IP_MULTICAST_IF, networkInterface);

InetAddress group = InetAddress.getByName("225.4.5.6");

MembershipKey key = dc.join(group, networkInterface);</pre>

你可以使用以前經常使用的DatagramChannel,但操作方式是多播了,因此你收到的是接口中所有的數據包,你發送的數據包會發到所有組。

NetworkChannel

現在所有網絡通道都實現了新的NetworkChannel接口,你可以輕松綁定套接字管道,設置和查詢套接字選項,此外,套接字選項也被擴展了,因此你可以使用操作系統特定的選項,對于高性能服務器這非常有用。

異步I/O

現在我們介紹最重要的新特性:異步I/O API,從它的名字我們就知道它有什么功能了,這個新的通道為套接字和文件提供了異步操作。當然,所有操作都是非阻塞的,但對所有異步通道,你也可以執行阻塞操作,所有異步I/O操作都有下列兩種形式中的一種:
  • 第一個返回java.util.concurrent.Future,代表等待結果,你可以使用Future特性等待I/O操作結束;
  • 第二個是使用CompletionHandler創建的,當操作結束時,如回調系統,調用這個處理程序。
下面是它們的一些例子,首先來看看使用Future的例子:
AsynchronousFileChannel channel = AsynchronousFileChannel.open(Paths.get("Path to file"));
ByteBuffer buffer = ByteBuffer.allocate(capacity);

Future result = channel.read(buffer, 100); //Read capacity bytes from the file starting at position 100

boolean done = result.isDone(); //Indicate if the result is already terminated</pre>你也可以等待結束:

int bytesRead = result.get();
或等待超時:
int bytesRead = result.get(10, TimeUnit.SECONDS); //Wait at most 10 seconds on the result
再來看看使用CompletionHandler的例子:
Future result = channel.read(buffer, 100, null, new CompletionHandler(){
    public void completed(Integer result, Object attachement){
        //Compute the result
    }

public void failed(Throwable exception, Object attachement){
    //Answer to the fail
}

});</pre>正如你所看到的,你可以給操作一個附件,在操作末尾給CompletionHandler,當然,你可以把null當作一個附件提供,你可以傳遞任何你想傳遞的,如用于AsynchronousSocketChannel的Connection,或用于讀操作的ByteBuffer。

Future result = channel.read(buffer, 100, buffer, new CompletionHandler(){
    public void completed(Integer result, ByteBuffer buffer){
        //Compute the result
    }

public void failed(Throwable exception, ByteBuffer buffer){
    //Answer to the fail
}

});</pre>

正如你所看到的,CompletionHandle也提供Future元素表示等待結果,因此你可以合并這兩個格式。下面是NIO.2中的所有異步通道:
    • AsynchronousFileChannel:讀寫文件的異步通道,這個通道沒有全局位置,因此每個讀寫操作都需要一個位置,你可以使用不同的線程同時訪問文件的不同部分,當你打開這個通道時,必須指定READ或WRITE選項;
    • AsynchronousSocketChannel:用于套接字的一個簡單異步通道,連接、讀/寫、分散/聚集方法全都是異步的,讀/寫方法支持超時;
    • AsynchronousServerSocketChannel:用于ServerSocket的異步通道,accept()方法是異步的,當連接被接受時,調用CompletionHandler,這種連接的結果是一個AsynchronousSocketChannel;
    • AsynchronousDatagramChannel:用于數據報套接字的通道,讀/寫(連接)和接收/發送(無連接)方法是異步的。

分組

當你使用AsynchronousChannels時,有線程調用完整的處理程序,這些線程被綁定到一個 AsynchronousChannelGroup組,這個組包含一個線程池,它封裝了所有線程共享的資源,你可以使用線程池來調用這些組,AsynchronousFileChannel可以使用它自己的組創建,將ExecutorService作為一個參數傳遞給open()方法,在 open方法中,通道是使用AsynchronousChannelGroup創建的,如果你不給它一個組,或傳遞一個NULL,它就會使用默認組。通道被認為是屬于組的,因此,如果組關閉了,通道也就關閉了。你可以使用ThreadFactory創建一個組:
ThreadFactory myThreadFactory = Executors.defaultThreadFactory();

AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withFixedThreadPool(25, threadFactory);</pre>或使用一個ExecutorService:

ExecutorService service = Executors.newFixedThreadPool(25);

AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withThreadPool(service);</pre>而且你可以很容易地使用它:

AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open(channelGroup);
你可以使用在組上使用shutdown()方法關閉組,關閉之后,你就不能使用這個組創建更多的通道,當所有通道關閉后,組也終止了,處理程序結束,資源也釋放了。
當你使用任何類型的池和CompletionHandler時,你必須要注意一點,不要在CompletionHandler內使用阻塞或長時間操作,如果所有線程都被阻塞,整個應用程序都會被阻塞掉。如果你有自定義或緩存的線程池,它會使隊列無限制地增長,最終導致OutOfMemoryError。
我想我把新的異步I/O API涵蓋的內容講得差不多了,當然這不是一兩句話可以說清楚的,也不是每一個人都會使用到它們,但在某些時候它確實很有用,Java支持這種I/O操作終歸是一件好事。如果我的代碼中有什么錯誤我表示道歉,因為我也是剛剛才接觸。
中文原文: 51CTO
</div> </div> </div> </div> </div>

 本文由用戶 fmms 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!