導航:首頁 > 編程語言 > java非阻模式

java非阻模式

發布時間:2024-04-16 11:27:52

java 中 阻塞隊列 非阻塞隊列 和普通隊列的區別是什麼

阻塞隊列與普通隊列的區別在於,當隊列是空的時,從隊列中獲取元素的操作將會被阻塞,或者當隊列是滿時,往隊列里添加元素的操作會被阻塞。試圖從空的阻塞隊列中獲取元素的線程將會被阻塞,直到其他的線程往空的隊列插入新的元素。同樣,試圖往已滿的阻塞隊列中添加新元素的線程同樣也會被阻塞,直到其他的線程使隊列重新變得空閑起來,如從隊列中移除一個或者多個元素,或者完全清空隊列.
1.ArrayDeque, (數組雙端隊列)
2.PriorityQueue, (優先順序隊列)
3.ConcurrentLinkedQueue, (基於鏈表的並發隊列)
4.DelayQueue, (延期阻塞隊列)(阻塞隊列實現了BlockingQueue介面)
5.ArrayBlockingQueue, (基於數組的並發阻塞隊列)
6.LinkedBlockingQueue, (基於鏈表的FIFO阻塞隊列)
7.LinkedBlockingDeque, (基於鏈表的FIFO雙端阻塞隊列)
8.PriorityBlockingQueue, (帶優先順序的無界阻塞隊列)
9.SynchronousQueue (並發同步阻塞隊列)
阻塞隊列和生產者-消費者模式
阻塞隊列(Blocking queue)提供了可阻塞的put和take方法,它們與可定時的offer和poll是等價的。如果Queue已經滿了,put方法會被阻塞直到有空間可用;如果Queue是空的,那麼take方法會被阻塞,直到有元素可用。Queue的長度可以有限,也可以無限;無限的Queue永遠不會充滿,所以它的put方法永遠不會阻塞。
阻塞隊列支持生產者-消費者設計模式。一個生產者-消費者設計分離了「生產產品」和「消費產品」。該模式不會發現一個工作便立即處理,而是把工作置於一個任務(「to do」)清單中,以備後期處理。生產者-消費者模式簡化了開發,因為它解除了生產者和消費者之間相互依賴的代碼。生產者和消費者以不同的或者變化的速度生產和消費數據,生產者-消費者模式將這些活動解耦,因而簡化了工作負荷的管理。
生產者-消費者設計是圍繞阻塞隊列展開的,生產者把數據放入隊列,並使數據可用,當消費者為適當的行為做准備時會從隊列中獲取數據。生產者不需要知道消費者的省份或者數量,甚至根本沒有消費者—它們只負責把數據放入隊列。類似地,消費者也不需要知道生產者是誰,以及是誰給它們安排的工作。BlockingQueue可以使用任意數量的生產者和消費者,從而簡化了生產者-消費者設計的實現。最常見的生產者-消費者設計是將線程池與工作隊列相結合。
阻塞隊列簡化了消費者的編碼,因為take會保持阻塞直到可用數據出現。如果生產者不能足夠快地產生工作,讓消費者忙碌起來,那麼消費者只能一直等待,直到有工作可做。同時,put方法的阻塞特性也大大地簡化了生產者的編碼;如果使用一個有界隊列,那麼當隊列充滿的時候,生產者就會阻塞,暫不能生成更多的工作,從而給消費者時間來趕進進度。
有界隊列是強大的資源管理工具,用來建立可靠的應用程序:它們遏制那些可以產生過多工作量、具有威脅的活動,從而讓你的程序在面對超負荷工作時更加健壯。
雖然生產者-消費者模式可以把生產者和消費者的代碼相互解耦合,但是它們的行為還是間接地通過共享隊列耦合在一起了
類庫中包含一些BlockingQueue的實現,其中LinkedBlockingQueue和ArrayBlockingQueue是FIFO隊列,與 LinkedList和ArrayList相似,但是卻擁有比同步List更好的並發性能。PriorityBlockingQueue是一個按優先順序順序排序的隊列,當你不希望按照FIFO的屬性處理元素時,這個PriorityBolckingQueue是非常有用的。正如其他排序的容器一樣,PriorityBlockingQueue可以比較元素本身的自然順序(如果它們實現了Comparable),也可以使用一個 Comparator進行排序。
最後一個BlockingQueue的實現是SynchronousQueue,它根本上不是一個真正的隊列,因為它不會為隊列元素維護任何存儲空間。不過,它維護一個排隊的線程清單,這些線程等待把元素加入(enqueue)隊列或者移出(dequeue)隊列。因為SynchronousQueue沒有存儲能力,所以除非另一個線程已經准備好參與移交工作,否則put和take會一直阻止。SynchronousQueue這類隊列只有在消費者充足的時候比較合適,它們總能為下一個任務作好准備。
非阻塞演算法
基於鎖的演算法會帶來一些活躍度失敗的風險。如果線程在持有鎖的時候因為阻塞I/O,頁面錯誤,或其他原因發生延遲,很可能所有的線程都不能前進了。
一個線程的失敗或掛起不應該影響其他線程的失敗或掛起,這樣的演算法成為非阻塞(nonblocking)演算法;如果演算法的每一個步驟中都有一些線程能夠繼續執行,那麼這樣的演算法稱為鎖自由(lock-free)演算法。在線程間使用CAS進行協調,這樣的演算法如果能構建正確的話,它既是非阻塞的,又是鎖自由的。非競爭的CAS總是能夠成功,如果多個線程以一個CAS競爭,總會有一個勝出並前進。非阻塞演算法堆死鎖和優先順序倒置有「免疫性」(但它們可能會出現飢餓和活鎖,因為它們允許重進入)。
非阻塞演算法通過使用低層次的並發原語,比如比較交換,取代了鎖。原子變數類向用戶提供了這些底層級原語,也能夠當做「更佳的volatile變數」使用,同時提供了整數類和對象引用的原子化更新操作

❷ java怎麼寫阻塞式io流,阻塞與非阻塞在寫法上的有什麼區別求大神

首先要明白什麼是「阻塞」?
阻塞實際是針對「當前」線程的一個概念,當前線程可以往下走,就是沒有阻塞,否則就可以說當前線程被阻塞了。
明白了概念就好處理了:
非阻塞:new Thread(){ public void run(){ /* 我的IO處理*/ } }.start()
阻塞:aInputStream.read()這樣就可以了。最簡單的驗證:在main方法中加入這句「System.in.read();」看看你的程序是不是停在這句了?除非你在控制台輸入東西,否則你的程序就「阻塞」在這里了。

❸ Java NIO怎麼理解通道和非阻塞

nio引入了buffer、channel、selector等概念。
通道相當於之前的I/O流。
「通道」太抽象了。java解釋不清的東西只能看它底層是怎麼解釋的——操作系統的I/O控制,通道控制方式?
I/O設備:CPU——通道——設備控制器——I/O設備
(通道和設備控制器的關系是多對多,設備控制器和I/O設備的關系也是多對多。)
I/O過程,參考http://www.nbrkb.net/lwt/jsjsj/asm/INTR&DMA.htm:
1.CPU在執行用戶程序時遇到I/O請求,根據用戶的I/O請求生成通道程序(也可以是事先編好的)。放到內存中,並把該通道程序首地址放入CAW中。
2.CPU執行「啟動I/O」指令,啟動通道工作。
3.通道接收「啟動I/O」指令信號,從CAW(記錄下一條通道指令存放的地址)中取出通道程序首地址,並根據此地址取出通道程序的第一條指令,放入CCW(記錄正在執行的通道指令)中;同時向CPU發回答信號,通知「啟動I/O」指令完成完畢,CPU可繼續執行。
4.與此同時,通道開始執行通道程序,進行物理I/O操作。當執行完一條指令後,如果還有下一條指令則繼續執行;否則表示傳輸完成,同時自行停止,通知CPU轉去處理通道結束事件,並從CCW中得到有關通道狀態。

如此一來,主處理器只要發出一個I/O操作命令,剩下的工作完全由通道負責。I/O操作結束後,I/O通道會發出一個中斷請求,表示相應操作已完成。

通道控制方式是對數據塊進行處理的,並非位元組。

通道控制方式就是非同步I/O,參考http://blog.csdn.net/historyasamirror/article/details/5778378:
I/O分兩段:1.數據從I/O設備到內核緩沖區。2.數據從內核緩沖區到應用緩沖區

I/O類型:
1.非同步I/O不會產生阻塞,程序不會等待I/O完成,繼續執行代碼,等I/O完成了再執行一個什麼回調函數,代碼執行效率高。很容易聯想到ajax。這個一般用於I/O操作不影響之後的代碼執行。
2.阻塞I/O,程序發起I/O操作後,進程阻塞,CPU轉而執行其他進程,I/O的兩個步驟完成後,向CPU發送中斷信號,進程就緒,等待執行。
3.非阻塞I/O並非都不阻塞,其實是第一步不阻塞,第二部阻塞。程序發起I/O操作後,進程一直檢查第一步是否完成,CPU一直在循環詢問,完成後,進程阻塞直到完成第二步。明白了!這個是「站著茅坑不拉屎」,CPU利用率最低的。邏輯和操作系統的程序直接控制方式一樣。
阻塞不阻塞描述的是發生I/O時當前線程的狀態。
以上是操作系統的I/O,那麼java的nio又是怎樣的呢?
個人覺得是模仿了通道控制方式。
先看看nio的示例代碼:
服務端TestReadServer.java

import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; public class TestReadServer { /*標識數字*/ private int flag = 0; /*緩沖區大小*/ private int BLOCK = 1024*1024*10; /*接受數據緩沖區*/ private ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK); /*發送數據緩沖區*/ private ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK); private Selector selector; public TestReadServer(int port) throws IOException { // 打開伺服器套接字通道 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 伺服器配置為非阻塞 serverSocketChannel.configureBlocking(false); // 檢索與此通道關聯的伺服器套接字 ServerSocket serverSocket = serverSocketChannel.socket(); // 進行服務的綁定 serverSocket.bind(new InetSocketAddress(port)); // 通過open()方法找到Selector selector = Selector.open(); // 注冊到selector,等待連接 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("Server Start----"+port+":"); } // 監聽 private void listen() throws IOException { while (true) { // 選擇一組鍵,並且相應的通道已經打開 selector.select(); // 返回此選擇器的已選擇鍵集。 Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey selectionKey = iterator.next(); iterator.remove(); handleKey(selectionKey); } } } // 處理請求 private void handleKey(SelectionKey selectionKey) throws IOException { // 接受請求 ServerSocketChannel server = null; SocketChannel client = null; String receiveText; String sendText; int count=0; // 測試此鍵的通道是否已准備好接受新的套接字連接。 if (selectionKey.isAcceptable()) { // 返回為之創建此鍵的通道。 server = (ServerSocketChannel) selectionKey.channel(); // 接受到此通道套接字的連接。 // 此方法返回的套接字通道(如果有)將處於阻塞模式。 client = server.accept(); // 配置為非阻塞 client.configureBlocking(false); // 注冊到selector,等待連接 client.register(selector, SelectionKey.OP_READ); } else if (selectionKey.isReadable()) { // 返回為之創建此鍵的通道。 client = (SocketChannel) selectionKey.channel(); //將緩沖區清空以備下次讀取 receivebuffer.clear(); //讀取伺服器發送來的數據到緩沖區中 System.out.println(System.currentTimeMillis()); count = client.read(receivebuffer); System.out.println(System.currentTimeMillis() + "~"+count); } } /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // TODO Auto-generated method stub int port = 1234; TestReadServer server = new TestReadServer(port); server.listen(); } }客戶端TestReadClient.javaimport java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; public class TestReadClient { /*標識數字*/ private static int flag = 0; /*緩沖區大小*/ private static int BLOCK = 1024*1024*10; /*接受數據緩沖區*/ private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK); /*發送數據緩沖區*/ private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK); /*伺服器端地址*/ private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress( "localhost", 1234); public static void main(String[] args) throws IOException { // TODO Auto-generated method stub // 打開socket通道 SocketChannel socketChannel = SocketChannel.open(); // 設置為非阻塞方式 socketChannel.configureBlocking(false); // 打開選擇器 Selector selector = Selector.open(); // 注冊連接服務端socket動作 socketChannel.register(selector, SelectionKey.OP_CONNECT); // 連接 socketChannel.connect(SERVER_ADDRESS); // 分配緩沖區大小內存 Set<SelectionKey> selectionKeys; Iterator<SelectionKey> iterator; SelectionKey selectionKey; SocketChannel client; String receiveText; String sendText; int count=0; while (true) { //選擇一組鍵,其相應的通道已為 I/O 操作準備就緒。 //此方法執行處於阻塞模式的選擇操作。 selector.select(); //返回此選擇器的已選擇鍵集。 selectionKeys = selector.selectedKeys(); //System.out.println(selectionKeys.size()); iterator = selectionKeys.iterator(); while (iterator.hasNext()) { selectionKey = iterator.next(); if (selectionKey.isConnectable()) { System.out.println("client connect"); client = (SocketChannel) selectionKey.channel(); // 判斷此通道上是否正在進行連接操作。 // 完成套接字通道的連接過程。 if (client.isConnectionPending()) { client.finishConnect(); System.out.println("完成連接!"); sendbuffer.clear(); BufferedInputStream br = new BufferedInputStream(new FileInputStream(new File("D:\BigData.zip"))); byte[] b = new byte[BLOCK]; br.read(b); sendbuffer.put(b); sendbuffer.flip(); System.out.println(System.currentTimeMillis()); client.write(sendbuffer); System.out.println(System.currentTimeMillis()); } client.register(selector, SelectionKey.OP_READ); } else if (selectionKey.isReadable()) { client = (SocketChannel) selectionKey.channel(); //將緩沖區清空以備下次讀取 receivebuffer.clear(); //讀取伺服器發送來的數據到緩沖區中 count=client.read(receivebuffer); if(count>0){ receiveText = new String( receivebuffer.array(),0,count); System.out.println("客戶端接受伺服器端數據--:"+receiveText); client.register(selector, SelectionKey.OP_WRITE); } } } selectionKeys.clear(); } } }例子是TestReadClient向TestReadServer發送一個本地文件。TestReadServer收到後每次列印讀取到的位元組數。

如何體現非同步I/O?
看看TestReadClient中的:

if (selectionKey.isConnectable()) { System.out.println("client connect"); client = (SocketChannel) selectionKey.channel(); // 判斷此通道上是否正在進行連接操作。 // 完成套接字通道的連接過程。 if (client.isConnectionPending()) { client.finishConnect();如果沒有client.finishConnect();這句等待完成socket連接,可能會報異常:java.nio.channels.NotYetConnectedException

非同步的才不會管你有沒有連接成功,都會執行下面的代碼。這里需要人為的干預。
如果要證明是java的nio單獨使用非阻塞I/O,真沒辦法!!!阻塞非阻塞要查看進程。。。
不過還有種說法,叫非同步非阻塞。上面那段,是用非同步方式創建連接,進程當然沒有被阻塞。使用了finishConnect()這是人為將程序中止,等待連接創建完成(是模仿阻塞將當前進程阻塞掉,還是模仿非阻塞不斷輪詢訪問,不重要了反正是程序卡住沒往下執行)。
所以,創建連接的過程用非同步非阻塞I/O可以解釋的通。那read/write的過程呢?
根據上面例子的列印結果,可以知道這個過程是同步的,沒執行完是不會執行下面的代碼的。至於底下是使用阻塞I/O還是非阻塞I/O,對於應用級程序來說不重要了。
阻塞還是非阻塞,對於正常的開發(創立連接,從連接中讀寫數據)並沒有多少的提升,操作過程都類似。
那NIO憑什麼成為高性能架構的基礎,比起IO,性能優越在哪裡,接著猜。。。
java nio有意模仿操作系統的通道控制方式,那他的底層是不是就是直接使用操作系統的通道?
通道中的數據是以塊為單位的,之前的流是以位元組為單位的,同樣的數據流操作外設的次數較多。代碼中channel都是針對ByteBuffer對象進行read/write的,而ByteBuffer又是ByteBuffer.allocate(BLOCK);這樣創建的,是一個連續的塊空間。
那ByteBuffer是不是也是模擬操作系統的緩存?
緩存在io也有,如BufferedInputStream。CPU和外設的速度差很多,緩存為了提高CPU使用率,等外設將數據讀入緩存後,CPU再統一操作,不用外設讀一次,CPU操作一次,CPU的效率會被拉下來。。。

❹ Java NIO和IO的區別

JavaNIO和IO之間的主要差別,我會更詳細地描述表中每部分的差異。
IONIO
面向流面向緩沖
阻塞IO非阻塞IO
無選擇器
面向流與面向緩沖
JavaNIO和IO之間第一個最大的區別是,IO是面向流的,NIO是面向緩沖區的。JavaIO面向流意味著每次從流中讀一個或多個位元組,直至讀取所有位元組,它們沒有被緩存在任何地方。此外,它不能前後移動流中的數據。如果需要前後移動從流中讀取的數據,需要先將它緩存到一個緩沖區。JavaNIO的緩沖導向方法略有不同。數據讀取到一個它稍後處理的緩沖區,需要時可在緩沖區中前後移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該緩沖區中包含所有您需要處理的數據。而且,需確保當更多的數據讀入緩沖區時,不要覆蓋緩沖區里尚未處理的數據。
阻塞與非阻塞IO
JavaIO的各種流是阻塞的。這意味著,當一個線程調用read()或write()時,該線程被阻塞,直到有一些數據被讀取,或數據完全寫入。該線程在此期間不能再干任何事情了。JavaNIO的非阻塞模式,使一個線程從某通道發送請求讀取數據,但是它僅能得到目前可用的數據,如果目前沒有數據可用時,就什麼都不會獲取。而不是保持線程阻塞,所以直至數據變的可以讀取之前,該線程可以繼續做其他的事情。非阻塞寫也是如此。一個線程請求寫入一些數據到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情。線程通常將非阻塞IO的空閑時間用於在其它通道上執行IO操作,所以一個單獨的線程現在可以管理多個輸入和輸出通道(channel)。
選擇器(Selectors)
JavaNIO的選擇器允許一個單獨的線程來監視多個輸入通道,你可以注冊多個通道使用一個選擇器,然後使用一個單獨的線程來逗選擇地通道:這些通道里已經有可以處理的輸入,或者選擇已准備寫入的通道。這種選擇機制,使得一個單獨的線程很容易來管理多個通道。
NIO和IO如何影響應用程序的設計
無論您選擇IO或NIO工具箱,可能會影響您應用程序設計的以下幾個方面:
1.對NIO或IO類的API調用。
2.數據處理。
3.用來處理數據的線程數。
API調用
當然,使用NIO的API調用時看起來與使用IO時有所不同,但這並不意外,因為並不是僅從一個InputStream逐位元組讀取,而是數據必須先讀入緩沖區再處理。
數據處理
使用純粹的NIO設計相較IO設計,數據處理也受到影響。
在IO設計中,我們從InputStream或Reader逐位元組讀取數據。假設你正在處理一基於行的文本數據流,例如:
Name:Anna
Age:25
Email:[email protected]
Phone:1234567890
該文本行的流可以這樣處理:
BufferedReaderreader=newBufferedReader(newInputStreamReader(input));

StringnameLine=reader.readLine();
StringageLine=reader.readLine();
StringemailLine=reader.readLine();
StringphoneLine=reader.readLine();
請注意處理狀態由程序執行多久決定。換句話說,一旦reader.readLine()方法返回,你就知道肯定文本行就已讀完,readline()阻塞直到整行讀完,這就是原因。你也知道此行包含名稱;同樣,第二個readline()調用返回的時候,你知道這行包含年齡等。正如你可以看到,該處理程序僅在有新數據讀入時運行,並知道每步的數據是什麼。一旦正在運行的線程已處理過讀入的某些數據,該線程不會再回退數據(大多如此)。下圖也說明了這條原則:
(JavaIO:從一個阻塞的流中讀數據)而一個NIO的實現會有所不同,下面是一個簡單的例子:
ByteBufferbuffer=ByteBuffer.allocate(48);

intbytesRead=inChannel.read(buffer);
注意第二行,從通道讀取位元組到ByteBuffer。當這個方法調用返回時,你不知道你所需的所有數據是否在緩沖區內。你所知道的是,該緩沖區包含一些位元組,這使得處理有點困難。
假設第一次read(buffer)調用後,讀入緩沖區的數據只有半行,例如,逗Name:An地,你能處理數據嗎看顯然不能,需要等待,直到整行數據讀入緩存,在此之前,對數據的任何處理毫無意義。
所以,你怎麼知道是否該緩沖區包含足夠的數據可以處理呢看好了,你不知道。發現的方法只能查看緩沖區中的數據。其結果是,在你知道所有數據都在緩沖區里之前,你必須檢查幾次緩沖區的數據。這不僅效率低下,而且可以使程序設計方案雜亂不堪。例如:
ByteBufferbuffer=ByteBuffer.allocate(48);

intbytesRead=inChannel.read(buffer);

while(!bufferFull(bytesRead)){

bytesRead=inChannel.read(buffer);

}
bufferFull()方法必須跟蹤有多少數據讀入緩沖區,並返回真或假,這取決於緩沖區是否已滿。換句話說,如果緩沖區准備好被處理,那麼表示緩沖區滿了。
bufferFull()方法掃描緩沖區,但必須保持在bufferFull()方法被調用之前狀態相同。如果沒有,下一個讀入緩沖區的數據可能無法讀到正確的位置。這是不可能的,但卻是需要注意的又一問題。
如果緩沖區已滿,它可以被處理。如果它不滿,並且在你的實際案例中有意義,你或許能處理其中的部分數據。但是許多情況下並非如此。下圖展示了逗緩沖區數據循環就緒地:
3)用來處理數據的線程數
NIO可讓您只使用一個(或幾個)單線程管理多個通道(網路連接或文件),但付出的代價是解析數據可能會比從一個阻塞流中讀取數據更復雜。
如果需要管理同時打開的成千上萬個連接,這些連接每次只是發送少量的數據,例如聊天伺服器,實現NIO的伺服器可能是一個優勢。同樣,如果你需要維持許多打開的連接到其他計算機上,如P2P網路中,使用一個單獨的線程來管理你所有出站連接,可能是一個優勢。一個線程多個連接的設計方案如
JavaNIO:單線程管理多個連接
如果你有少量的連接使用非常高的帶寬,一次發送大量的數據,也許典型的IO伺服器實現可能非常契合。下圖說明了一個典型的IO伺服器設計:

JavaIO:一個典型的IO伺服器設計-一個連接通過一個線程處理

❺ java socket 阻塞和非阻塞 會對數據傳輸有什麼影響

阻塞會導致你的程序停在這里不往下走 非阻塞時,你的程序可以一邊干其他事情(比如顯示個進度條),以便等數據過來。

❻ Java網路編程從入門到精通(33):非阻塞I/O的緩沖區(Buffer)

如果將同步I/O方式下的數據傳輸比做數據傳輸的零星方式(這里的零星是指在數據傳輸的過程中是以零星的位元組方式進行的) 那麼就可以將非阻塞I/O方式下的數據傳輸比做數據傳輸的集裝箱方式(在位元組和低層數據傳輸之間 多了一層緩沖區 因此 可以將緩沖區看做是裝載位元組的集裝箱) 大家可以想像 如果我們要運送比較少的貨物 用集裝箱好象有點不太合算 而如果要運送上百噸的貨物 用集裝箱來運送的成本會更低 在數據傳輸過程中也是一樣 如果數據量很小時 使用同步I/O方式會更適合 如果數據量很大時(一般以G為單位) 使用非阻塞I/O方式的效率會更高 因此 從理論上說 數據量越大 使用非阻塞I/O方式的單位成本就會越低 產生這種結果的原因和緩沖區的一些特性有著直接的關系 在本節中 將對緩沖區的一些主要特性進行講解 使讀者可以充分理解緩沖區的概念沖森 並能通過緩沖區來提高程序的執行效率

創建緩沖區

Java提供了七個基本的緩沖區 分別由七個類來擾判拍管理 它們都可以在java nio包中找到 這七個類如下所示

ByteBuffer

ShortBuffer

IntBuffer

CharBuffer

FloatBuffer

DoubleBuffer

LongBuffer

這七個類中的方法類似 只是它們的返回值或參數和相應的簡單類型相對應 如ByteBuffer類的get方法返回了byte類型的數據 而put方法需要一個byte類型的參數 在CharBuffer類中的get和put方法返回和傳遞的數據類型就是char 這七個類都沒有public構造方法 因此 它們不能通過new來創建相應的對象實例 這些類都可以通過兩種方式來創建相應的對象實例

通過靜態方法allocate來創建緩沖區

這七類都有一個靜態的allocate方法 通過這個方法可以創建有最大容量限制的緩沖區對象 allocate的定義如下

ByteBuffer類中的allocate方法

(intcapacity)

IntBuffer類中的allocate方法

publicstaticIntBufferallocate(intcapacity)

其他五個緩沖區類中的allocate 方法定義和上面的定義類似 只是返回值的類型是相應的緩沖區緩羨類

allocate方法有一個參數capacity 用來指定緩沖區容量的最大值 capacity的不能小於 否則會拋出一個IllegalArgumentException異常 使用allocate來創建緩沖區 並不是一下子就分配給緩沖區capacity大小的空間 而是根據緩沖區中存儲數據的情況來動態分配緩沖區的大小(實際上 在低層Java採用了數據結構中的堆來管理緩沖區的大小) 因此 這個capacity可以是一個很大的值 如 * ( M) allocate的使用方法如下

ByteBufferbyteBuffer=ByteBuffer allocate( );IntBufferintBuffer=IntBuffer allocate( );

在使用allocate創建緩沖區時應用注意 capacity的含義隨著緩沖區的不同而不同 如創建位元組緩沖區時 capacity指的是位元組數 而在創建整型(int)緩沖區時 capacity指的是int型值的數目 如果轉換成字數 capacity的值應該乘 如上面代碼中的intBuffer緩沖區最大可容納的位元組數是 * = 個

通過靜態方法wrap來創建緩沖區

使用allocate方法可以創建一個空的緩沖區 而wrap方法可以利用已經存在的數據來創建緩沖區 wrap方法可以將數組直接轉換成相應類型的緩沖區 wrap方法有兩種重載形式 它們的定義如下

ByteBuffer類中的wrap方法

publicstaticByteBufferwrap(byte[]array)publicstaticByteBufferwrap(byte[]array intoffset intlength)

IntBuffer類中的wrap方法

publicstaticIntBufferwrap(byte[]array)publicstaticIntBufferwrap(byte[]array intoffset intlength)

其他五個緩沖區類中的wrap 方法定義和上面的定義類似 只是返回值的類型是相應的緩沖區類

在wrap方法中的array參數是要轉換的數組(如果是其他的緩沖區類 數組的類型就是相應的簡單類型 如IntBuffer類中的wrap方法的array就是int[]類型) offset是要轉換的子數組的偏移量 也就是子數組在array中的開始索引 length是要轉換的子數組的長度 利用後兩個參數可以將array數組中的一部分轉換成緩沖區對象 它們的使用方法如下

byte[]myByte=newbyte[]{ };int[]myInt=newint[]{ };ByteBufferbyteBuffer=ByteBuffer wrap(myByte);IntBufferintBuffer=IntBuffer wrap(myInt );

可以通過緩沖區類的capacity方法來得到緩沖區的大小 capacity方法的定義如下

publicfinalintcapacity()

如果使用allocate方法來創建緩沖區 capacity方法的返回值就是capacity參數的值 而使用wrap方法來創建緩沖區 capacity方法的返回值是array數組的長度 但要注意 使用wrap來轉換array的字數組時 capacity的長度仍然是原數組的長度 如上面代碼中的intBuffer緩沖區的capacity值是 而不是

除了可以將數組轉換成緩沖區外 也可以通過緩沖區類的array方法將緩沖區轉換成相應類型的數組 IntBuffer類的array方法的定義方法如下(其他緩沖區類的array的定義類似)

publicfinalint[]array()

下面的代碼演示了如何使用array方法將緩沖區轉換成相應類型的數組

int[]myInt=newint[]{ };IntBufferintBuffer=IntBuffer wrap(myInt );for(intv:intBuffer array()) System out print(v+ );

在執行上面代碼後 我們發現輸出的結果是 而不是 這說明在將子數組轉換成緩沖區的過程中實際上是將整個數組轉換成了緩沖區 這就是用wrap包裝子數組後 capacity的值仍然是原數組長度的真正原因 在使用array方法時應注意 在以下兩種緩沖區中不能使用array方法

只讀的緩沖區如果使用只讀緩沖區的array方法 將會拋出一個ReadOnlyBufferException異常

使用allocateDirect方法創建的緩沖區

如果調用這種緩沖區中的array方法 將會拋出一個UnsupportedOperationException異常

可以通過緩沖區類的hasArray方法來判斷這個緩沖區是否可以使用array方法 如果返回true 則說明這個緩沖區可以使用array方法 否則 使用array方法將會拋出上述的兩種異常之一

注意 使用array方法返回的數組並不是緩沖區數據的副本 被返回的數組實際上就是緩沖區中的數據 也就是說 array方法只返回了緩沖區數據的引用 當數組中的數據被修改後 緩沖區中的數據也會被修改 返之也是如此 關於這方面內容將在下一節 讀寫緩沖區中的數據 中詳細講解

在上述的七個緩沖區類中 ByteBuffer類和CharBuffer類各自還有另外一種方法來創建緩沖區對象

ByteBuffer類

可以通過ByteBuffer類的allocateDirect方法來創建ByteBuffer對象 allocateDirect方法的定義如下

Direct(intcapacity)

使用allocateDirect方法可以一次性分配capacity大小的連續位元組空間 通過allocateDirect方法來創建具有連續空間的ByteBuffer對象雖然可以在一定程度上提高效率 但這種方式並不是平台獨立的 也就是說 在一些操作系統平台上使用allocateDirect方法來創建ByteBuffer對象會使效率大幅度提高 而在另一些操作系統平台上 性能會表現得非常差 而且allocateDirect方法需要較長的時間來分配內存空間 在釋放空間時也較慢 因此 在使用allocateDirect方法時應謹慎

通過isDirect方法可以判斷緩沖區對象(其他的緩沖區類也有isDirect方法 因為 ByteBuffer對象可以轉換成其他的緩沖區對象 這部分內容將在後面講解)是用哪種方式創建的 如果isDirect方法返回true 則這個緩沖區對象是用allocateDirect方法創建的 否則 就是用其他方法創建的緩沖區對象

CharBuffer類

我們可以發現 上述的七種緩沖區中並沒有字元串緩沖區 而字元串在程序中卻是最常用的一種數據類型 不過不要擔心 雖然java nio包中並未提供字元串緩沖區 但卻可以將字元串轉換成字元緩沖區(就是CharBuffer對象) 在CharBuffer類中的wrap方法除了上述的兩種重載形式外 又多了兩種重載形式 它們的定義如下

publicstaticCharBufferwrap(CharSequencecsq)publicstaticCharBufferwrap(CharSequencecsq intstart intend)

其中csq參數表示要轉換的字元串 但我們注意到csq的類型並不是String 而是CharSequence CharSequence類Java中四個可以表示字元串的類的父類 這四個類是String StringBuffer StringBuilder和CharBuffer(大家要注意 StringBuffer和本節講的緩沖區類一點關系都沒有 這個類在java lang包中) 也就是說 CharBuffer類的wrap方法可以將這四個類的對象轉換成CharBuffer對象

另外兩個參數start和end分別是子字元串的開始索引和結束索引的下一個位置 如將字元串 中的 轉換成CharBuffer對象的語句如下

CharBuffercb=CharBuffer wrap( );

下面的代碼演示了如何使用wrap方法將不同形式的字元串轉換成CharBuffer對象

lishixin/Article/program/Java/hx/201311/26505

閱讀全文

與java非阻模式相關的資料

熱點內容
吃甜品點心解壓的聲音 瀏覽:579
蘋果軟體安卓手機如何下載 瀏覽:129
寶書網txt下載全文 瀏覽:519
尺度大的Les電影 瀏覽:309
免費V片在線觀看 瀏覽:726
word無法轉pdf 瀏覽:911
玩具現實版解壓視頻 瀏覽:924
哺乳人妻電影日本在線播放 瀏覽:489
科技編譯原理動畫 瀏覽:643
精彩發現app里怎麼投保意外險 瀏覽:145
美國電影愛情電影尺度大的 瀏覽:376
8個人在一個名為列車上 瀏覽:968
楊浦區少兒編程 瀏覽:944
免登錄php網盤源碼 瀏覽:422
王的命令在線觀看全集 瀏覽:927
西安市社保卡APP叫什麼 瀏覽:605
怎麼關掉華為手機自動更新app 瀏覽:906
韓國19禁 瀏覽:180
有部電影叫什麼長生 瀏覽:458
磁碟加密的過程中能使用嗎 瀏覽:352