什麼是零拷貝?
咱們首先來認識一下傳統的I/O操做。
假如說用戶進程如今要把一個文件複製到另外一個地方。
那麼用戶程序必須先把這個文件讀入內存,而後再把內存裏的數據寫入另外一個文件。
不過文件讀入內存也不是直接讀入用戶進程的內存,而是先讀入操做系統內核的內存,而後再從操做系統內核的內存區讀到用戶進程的內存。
與之對應的是,寫文件也不是直接寫到磁盤上的文件,而是用戶進程先把本身內存的數據傳到操做系統內核的內存,而後再從操做系統內核的內存區寫到磁盤。而這其中涉及到諸多的系統調用。
所以看上去簡單的操做至少要分爲四部
1磁盤文件讀入操做系統
2操做系統讀到用戶進程
3用戶進程寫到操做系統
4操做系統寫入磁盤文件java
零拷貝和傳統I/O有和不一樣?
零拷貝就是指,傳輸一個文件的時候,不須要把文件讀到用戶進程再處理,而是直接把文件讀到操做系統一個內存區,而後再移動到操做系統的另外一個內存區,最後寫入文件。
這樣一來,步驟變成這樣:
1磁盤文件讀入操做系統
2操做系統把數據寫入操做系統另外一個區域
3操做系統寫入磁盤文件
雖然只少了一步,可是這裏不只減小了數據移動的時間損耗,並且減小了系統調用的次數,所以大大縮短了時間。
更加詳細的解釋請看https://blog.csdn.net/u010530...編程
java裏如何實現零拷貝呢?
這就要提及java nio中的FileChannel.transferTo()方法了,該方法是把FileChannel中的數據利用零靠的技術轉移到另外一個channel。這另外一個channel每每是FileChannel,不過SocketChannel也是能夠的:)。
簡單實現(靜態下載文件,不能根據用戶指令來更改下載的文件。)
代碼以下:
單線程版本:服務器
package qiuqi.filedownloadtest; import java.io.FileInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.*; import java.util.Iterator; public class FileServer { public static void main(String[] args) throws IOException { startServer(); } public static void startServer() throws IOException { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(9999)); serverSocketChannel.configureBlocking(false); Selector selector = Selector.open(); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (selector.select() > 0) { Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); iterator.remove(); if(key.isAcceptable()) { SocketChannel socketChannel = serverSocketChannel.accept(); try (FileInputStream in = new FileInputStream("C:\\Users\\dell\\Desktop\\ZOL手機數據(1).rar")){ long size = in.available(); long num = 0; long begin = 0; while ( (num = in.getChannel().transferTo(begin,size,socketChannel))!=0) { size-=num; begin += num; } socketChannel.close(); } catch (IOException e){e.printStackTrace();} } } } } }
多線程版本:多線程
package qiuqi.filedownloadtest; import java.io.FileInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.*; import java.util.Iterator; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class FileServer { static ExecutorService threadpool = Executors.newCachedThreadPool(); public static void main(String[] args) throws IOException { startServer(); } public static void startServer() throws IOException { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(9999)); serverSocketChannel.configureBlocking(false); Selector selector = Selector.open(); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (selector.select() > 0) { Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); iterator.remove(); if(key.isAcceptable()) { SocketChannel socketChannel = serverSocketChannel.accept(); threadpool.execute(new Runnable() { @Override public void run() { try (FileInputStream in = new FileInputStream("C:\\Users\\dell\\Desktop\\ZOL手機數據(1).rar")){ long size = in.available(); long num = 0; long begin = 0; while ( (num = in.getChannel().transferTo(begin,size,socketChannel))!=0) { size-=num; begin += num; } socketChannel.close(); } catch (IOException e){e.printStackTrace();} } }); } } } } }
代碼就不講解了。若是學過java nio,那麼理解上面的程序垂手可得。
若是不熟悉java nio的服務器編程那麼請先學習再來觀看。socket
最後我想說,java NIO真的是NEW IO即新的IO,而不是NonBlocking IO即非阻塞IO。由於在這套體系裏,不單單提供了非阻塞的編程模型,並且提供了相似零拷貝,內存映射這樣的新技術(對於操做系統來講早就有了)。ide