精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Java I/O 操作及優化建議

開發 后端
I/O,即 Input/Output(輸入/輸出) 的簡稱。就 I/O 而言,概念上有 5 種模型:blocking I/O,nonblocking I/O,I/O multiplexing (select and poll),signal driven I/O (SIGIO),asynchronous I/O (the POSIX aio_functions)。不同的操作系統對上述模型支持不同,UNIX 支持 IO 多路復用。不同系統叫法不同,freebsd 里面叫 kqueue,Linux 叫 epoll。而 Windows2000 的時候就誕生了 IOCP 用以支持 asynchronous I/O。

Java I/O

I/O,即 Input/Output(輸入/輸出) 的簡稱。就 I/O 而言,概念上有 5 種模型:blocking I/O,nonblocking I/O,I/O multiplexing (select and poll),signal driven I/O (SIGIO),asynchronous I/O (the POSIX aio_functions)。不同的操作系統對上述模型支持不同,UNIX 支持 IO 多路復用。不同系統叫法不同,freebsd 里面叫 kqueue,Linux 叫 epoll。而 Windows2000 的時候就誕生了 IOCP 用以支持 asynchronous I/O。

Java 是一種跨平臺語言,為了支持異步 I/O,誕生了 NIO,Java1.4 引入的 NIO1.0 是基于 I/O 復用的,它在各個平臺上會選擇不同的復用方式。Linux 用的 epoll,BSD 上用 kqueue,Windows 上是重疊 I/O。

Java I/O 的相關方法如下所述:

  • 同步并阻塞 (I/O 方法):服務器實現模式為一個連接啟動一個線程,每個線程親自處理 I/O 并且一直等待 I/O 直到完成,即客戶端有連接請求時服務器端就需要啟動一個線程進行處理。但是如果這個連接不做任何事情就會造成不必要的線程開銷,當然可以通過線程池機制改 善這個缺點。I/O 的局限是它是面向流的、阻塞式的、串行的一個過程。對每一個客戶端的 Socket 連接 I/O 都需要一個線程來處理,而且在此期間,這個線程一直被占用,直到 Socket 關閉。在這期間,TCP 的連接、數據的讀取、數據的返回都是被阻塞的。也就是說這期間大量浪費了 CPU 的時間片和線程占用的內存資源。此外,每建立一個 Socket 連接時,同時創建一個新線程對該 Socket 進行單獨通信 (采用阻塞的方式通信)。這種方式具有很快的響應速度,并且控制起來也很簡單。在連接數較少的時候非常有效,但是如果對每一個連接都產生一個線程無疑是對 系統資源的一種浪費,如果連接數較多將會出現資源不足的情況;

  • 同步非阻塞 (NIO 方法):服務器實現模式為一個請求啟動一個線程,每個線程親自處理 I/O,但是另外的線程輪詢檢查是否 I/O 準備完畢,不必等待 I/O 完成,即客戶端發送的連接請求都會注冊到多路復用器上,多路復用器輪詢到連接有 I/O 請求時才啟動一個線程進行處理。NIO 則是面向緩沖區,非阻塞式的,基于選擇器的,用一個線程來輪詢監控多個數據傳輸通道,哪個通道準備好了 (即有一組可以處理的數據) 就處理哪個通道。服務器端保存一個 Socket 連接列表,然后對這個列表進行輪詢,如果發現某個 Socket 端口上有數據可讀時,則調用該 Socket 連接的相應讀操作;如果發現某個 Socket 端口上有數據可寫時,則調用該 Socket 連接的相應寫操作;如果某個端口的 Socket 連接已經中斷,則調用相應的析構方法關閉該端口。這樣能充分利用服務器資源,效率得到大幅度提高;

  • 異步非阻塞 (AIO 方法,JDK7 發布):服務器實現模式為一個有效請求啟動一個線程,客戶端的 I/O 請求都是由操作系統先完成了再通知服務器應用去啟動線程進行處理,每個線程不必親自處理 I/O,而是委派操作系統來處理,并且也不需要等待 I/O 完成,如果完成了操作系統會另行通知的。該模式采用了 Linux 的 epoll 模型。

在連接數不多的情況下,傳統 I/O 模式編寫較為容易,使用上也較為簡單。但是隨著連接數的不斷增多,傳統 I/O 處理每個連接都需要消耗一個線程,而程序的效率,當線程數不多時是隨著線程數的增加而增加,但是到一定的數量之后,是隨著線程數的增加而減少的。所以傳統 阻塞式 I/O 的瓶頸在于不能處理過多的連接。非阻塞式 I/O 出現的目的就是為了解決這個瓶頸。非阻塞 IO 處理連接的線程數和連接數沒有聯系,例如系統處理 10000 個連接,非阻塞 I/O 不需要啟動 10000 個線程,你可以用 1000 個,也可以用 2000 個線程來處理。因為非阻塞 IO 處理連接是異步的,當某個連接發送請求到服務器,服務器把這個連接請求當作一個請求“事件”,并把這個“事件”分配給相應的函數處理。我們可以把這個處理 函數放到線程中去執行,執行完就把線程歸還,這樣一個線程就可以異步的處理多個事件。而阻塞式 I/O 的線程的大部分時間都被浪費在等待請求上了。

Java NIO

Java.nio 包是 Java 在 1.4 版本之后新增加的包,專門用來提高 I/O 操作的效率。

表 1 所示是 I/O 與 NIO 之間的對比內容。

表 1. I/O VS NIO

I/O

NIO

面向流

面向緩沖

阻塞 IO

非阻塞 IO

選擇器

NIO 是基于塊 (Block) 的,它以塊為基本單位處理數據。在 NIO 中,最為重要的兩個組件是緩沖 Buffer 和通道 Channel。緩沖是一塊連續的內存塊,是 NIO 讀寫數據的中轉地。通道標識緩沖數據的源頭或者目的地,它用于向緩沖讀取或者寫入數據,是訪問緩沖的接口。Channel 是一個雙向通道,即可讀,也可寫。Stream 是單向的。應用程序不能直接對 Channel 進行讀寫操作,而必須通過 Buffer 來進行,即 Channel 是通過 Buffer 來讀寫數據的。

使用 Buffer 讀寫數據一般遵循以下四個步驟:

  1. 寫入數據到 Buffer;

  2. 調用 flip() 方法;

  3. 從 Buffer 中讀取數據;

  4. 調用 clear() 方法或者 compact() 方法。

當向 Buffer 寫入數據時,Buffer 會記錄下寫了多少數據。一旦要讀取數據,需要通過 flip() 方法將 Buffer 從寫模式切換到讀模式。在讀模式下,可以讀取之前寫入到 Buffer 的所有數據。

一旦讀完了所有的數據,就需要清空緩沖區,讓它可以再次被寫入。有兩種方式能清空緩沖區:調用 clear() 或 compact() 方法。clear() 方法會清空整個緩沖區。compact() 方法只會清除已經讀過的數據。任何未讀的數據都被移到緩沖區的起始處,新寫入的數據將放到緩沖區未讀數據的后面。

Buffer 有多種類型,不同的 Buffer 提供不同的方式操作 Buffer 中的數據。

圖 1 Buffer 接口層次圖

Java I/O 操作及優化建議

Buffer 寫數據有兩種情況:

  1. 從 Channel 寫到 Buffer,如例子中 Channel 從文件中讀取數據,寫到 Channel;

  2. 直接調用 put 方法,往里面寫數據。

從 Buffer 中讀取數據有兩種方式:

  1. 從 Buffer 讀取數據到 Channel;

  2. 使用 get() 方法從 Buffer 中讀取數據。

Buffer 的 rewin 方法將 position 設回 0,所以你可以重讀 Buffer 中的所有數據。limit 保持不變,仍然表示能從 Buffer 中讀取多少個元素(byte、char 等)。

clear() 和 compact() 方法

一旦讀完 Buffer 中的數據,需要讓 Buffer 準備好再次被寫入。可以通過 clear() 或 compact() 方法來完成。

如果調用的是 clear() 方法,position 將被設回 0,limit 被設置成 capacity 的值。換句話說,Buffer 被清空了。Buffer 中的數據并未清除,只是這些標記告訴我們可以從哪里開始往 Buffer 里寫數據。

如果 Buffer 中有一些未讀的數據,調用 clear() 方法,數據將“被遺忘”,意味著不再有任何標記會告訴你哪些數據被讀過,哪些還沒有。如果 Buffer 中仍有未讀的數據,且后續還需要這些數據,但是此時想要先寫些數據,那么使用 compact() 方法。compact() 方法將所有未讀的數據拷貝到 Buffer 起始處。然后將 position 設到最后一個未讀元素正后面。limit 屬性依然像 clear() 方法一樣,設置成 capacity。現在 Buffer 準備好寫數據了,但是不會覆蓋未讀的數據。

Buffer 參數

Buffer 有 3 個重要的參數:位置 (position)、容量 (capacity) 和上限 (limit)。

capacity 是指 Buffer 的大小,在 Buffer 建立的時候已經確定。

limit 當 Buffer 處于寫模式,指還可以寫入多少數據;處于讀模式,指還有多少數據可以讀。

position 當 Buffer 處于寫模式,指下一個寫數據的位置;處于讀模式,當前將要讀取的數據的位置。每讀寫一個數據,position+1,也就是 limit 和 position 在 Buffer 的讀/寫時的含義不一樣。當調用 Buffer 的 flip 方法,由寫模式變為讀模式時,limit(讀)=position(寫),position(讀) =0。

散射&聚集

NIO 提供了處理結構化數據的方法,稱之為散射 (Scattering) 和聚集 (Gathering)。散射是指將數據讀入一組 Buffer 中,而不僅僅是一個。聚集與之相反,指將數據寫入一組 Buffer 中。散射和聚集的基本使用方法和對單個 Buffer 操作時的使用方法相當類似。在散射讀取中,通道依次填充每個緩沖區。填滿一個緩沖區后,它就開始填充下一個,在某種意義上,緩沖區數組就像一個大緩沖區。 在已知文件具體結構的情況下,可以構造若干個符合文件結構的 Buffer,使得各個 Buffer 的大小恰好符合文件各段結構的大小。此時,通過散射讀的方式可以一次將內容裝配到各個對應的 Buffer 中,從而簡化操作。如果需要創建指定格式的文件,只要先構造好大小合適的 Buffer 對象,使用聚集寫的方式,便可以很快地創建出文件。清單 1 以 FileChannel 為例,展示如何使用散射和聚集讀寫結構化文件。

清單 1. 使用散射和聚集讀寫結構化文件

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NIOScatteringandGathering {
public void createFiles(String TPATH){
try {
ByteBuffer bookBuf = ByteBuffer.wrap("java 性能優化技巧".getBytes("utf-8"));
ByteBuffer autBuf = ByteBuffer.wrap("test".getBytes("utf-8"));
int booklen = bookBuf.limit();
int autlen = autBuf.limit();
ByteBuffer[] bufs = new ByteBuffer[]{bookBuf,autBuf};
File file = new File(TPATH);
if(!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
FileOutputStream fos = new FileOutputStream(file);
FileChannel fc = fos.getChannel();
fc.write(bufs);
fos.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

ByteBuffer b1 = ByteBuffer.allocate(booklen);
ByteBuffer b2 = ByteBuffer.allocate(autlen);
ByteBuffer[] bufs1 = new ByteBuffer[]{b1,b2};
File file1 = new File(TPATH);
try {
FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel();
fc.read(bufs1);
String bookname = new String(bufs1[0].array(),"utf-8");
String autname = new String(bufs1[1].array(),"utf-8");
System.out.println(bookname+" "+autname);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

public static void main(String[] args){
NIOScatteringandGathering nio = new NIOScatteringandGathering();
nio.createFiles("C://1.TXT");
}
}

輸出如下清單 2 所示。

清單 2. 運行結果

java 性能優化技巧 test

清單 3 所示代碼對傳統 I/O、基于 Byte 的 NIO、基于內存映射的 NIO 三種方式進行了性能上的對比,使用一個有 400 萬數據的文件的讀、寫操作耗時作為評測依據。

清單 3. I/O 的三種方式對比試驗

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class NIOComparator {
public void IOMethod(String TPATH){
long start = System.currentTimeMillis();
try {
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream(new File(TPATH))));
for(int i=0;i<4000000;i++){
dos.writeInt(i);//寫入 4000000 個整數
}
if(dos!=null){
dos.close();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
start = System.currentTimeMillis();
try {
DataInputStream dis = new DataInputStream(
new BufferedInputStream(new FileInputStream(new File(TPATH))));
for(int i=0;i<4000000;i++){
dis.readInt();
}
if(dis!=null){
dis.close();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

end = System.currentTimeMillis();
System.out.println(end - start);
}

public void ByteMethod(String TPATH){
long start = System.currentTimeMillis();
try {
FileOutputStream fout = new FileOutputStream(new File(TPATH));
FileChannel fc = fout.getChannel();//得到文件通道
ByteBuffer byteBuffer = ByteBuffer.allocate(4000000*4);//分配 Buffer
for(int i=0;i<4000000;i++){
byteBuffer.put(int2byte(i));//將整數轉為數組
}
byteBuffer.flip();//準備寫
fc.write(byteBuffer);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println(end - start);

start = System.currentTimeMillis();
FileInputStream fin;
try {
fin = new FileInputStream(new File(TPATH));
FileChannel fc = fin.getChannel();//取得文件通道
ByteBuffer byteBuffer = ByteBuffer.allocate(4000000*4);//分配 Buffer
fc.read(byteBuffer);//讀取文件數據
fc.close();
byteBuffer.flip();//準備讀取數據
while(byteBuffer.hasRemaining()){
byte2int(byteBuffer.get(),byteBuffer.get(),byteBuffer.get(),byteBuffer.get());//將 byte 轉為整數
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
end = System.currentTimeMillis();
System.out.println(end - start);
}

public void mapMethod(String TPATH){
long start = System.currentTimeMillis();
//將文件直接映射到內存的方法
try {
FileChannel fc = new RandomAccessFile(TPATH,"rw").getChannel();
IntBuffer ib = fc.map(FileChannel.MapMode.READ_WRITE, 0, 4000000*4).asIntBuffer();
for(int i=0;i<4000000;i++){
ib.put(i);
}
if(fc!=null){
fc.close();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println(end - start);

start = System.currentTimeMillis();
try {
FileChannel fc = new FileInputStream(TPATH).getChannel();
MappedByteBuffer lib = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
lib.asIntBuffer();
while(lib.hasRemaining()){
lib.get();
}
if(fc!=null){
fc.close();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
end = System.currentTimeMillis();
System.out.println(end - start);

}

public static byte[] int2byte(int res){
byte[] targets = new byte[4];
targets[3] = (byte)(res & 0xff);//最低位
targets[2] = (byte)((res>>8)&0xff);//次低位
targets[1] = (byte)((res>>16)&0xff);//次高位
targets[0] = (byte)((res>>>24));//最高位,無符號右移
return targets;
}

public static int byte2int(byte b1,byte b2,byte b3,byte b4){
return ((b1 & 0xff)<<24)|((b2 & 0xff)<<16)|((b3 & 0xff)<<8)|(b4 & 0xff);
}

public static void main(String[] args){
NIOComparator nio = new NIOComparator();
nio.IOMethod("c://1.txt");
nio.ByteMethod("c://2.txt");
nio.ByteMethod("c://3.txt");
}
}

清單 3 運行輸出如清單 4 所示。

清單 4. 運行輸出

1139
906
296
157
234
125

除上述描述及清單 3 所示代碼以外,NIO 的 Buffer 還提供了一個可以直接訪問系統物理內存的類 DirectBuffer。DirectBuffer 繼承自 ByteBuffer,但和普通的 ByteBuffer 不同。普通的 ByteBuffer 仍然在 JVM 堆上分配空間,其最大內存受到最大堆的限制,而 DirectBuffer 直接分配在物理內存上,并不占用堆空間。在對普通的 ByteBuffer 訪問時,系統總是會使用一個“內核緩沖區”進行間接的操作。而 DirectrBuffer 所處的位置,相當于這個“內核緩沖區”。因此,使用 DirectBuffer 是一種更加接近系統底層的方法,所以,它的速度比普通的 ByteBuffer 更快。DirectBuffer 相對于 ByteBuffer 而言,讀寫訪問速度快很多,但是創建和銷毀 DirectrBuffer 的花費卻比 ByteBuffer 高。DirectBuffer 與 ByteBuffer 相比較的代碼如清單 5 所示。

清單 5. DirectBuffer VS ByteBuffer

import java.nio.ByteBuffer;

public class DirectBuffervsByteBuffer {
public void DirectBufferPerform(){
long start = System.currentTimeMillis();
ByteBuffer bb = ByteBuffer.allocateDirect(500);//分配 DirectBuffer
for(int i=0;i<100000;i++){
for(int j=0;j<99;j++){
bb.putInt(j);
}
bb.flip();
for(int j=0;j<99;j++){
bb.getInt(j);
}
}
bb.clear();
long end = System.currentTimeMillis();
System.out.println(end-start);
start = System.currentTimeMillis();
for(int i=0;i<20000;i++){
ByteBuffer b = ByteBuffer.allocateDirect(10000);//創建 DirectBuffer
}
end = System.currentTimeMillis();
System.out.println(end-start);
}

public void ByteBufferPerform(){
long start = System.currentTimeMillis();
ByteBuffer bb = ByteBuffer.allocate(500);//分配 DirectBuffer
for(int i=0;i<100000;i++){
for(int j=0;j<99;j++){
bb.putInt(j);
}
bb.flip();
for(int j=0;j<99;j++){
bb.getInt(j);
}
}
bb.clear();
long end = System.currentTimeMillis();
System.out.println(end-start);
start = System.currentTimeMillis();
for(int i=0;i<20000;i++){
ByteBuffer b = ByteBuffer.allocate(10000);//創建 ByteBuffer
}
end = System.currentTimeMillis();
System.out.println(end-start);
}

public static void main(String[] args){
DirectBuffervsByteBuffer db = new DirectBuffervsByteBuffer();
db.ByteBufferPerform();
db.DirectBufferPerform();
}
}

運行輸出如清單 6 所示。

清單 6. 運行輸出

920
110
531
390

由清單 6 可知,頻繁創建和銷毀 DirectBuffer 的代價遠遠大于在堆上分配內存空間。使用參數-XX:MaxDirectMemorySize=200M –Xmx200M 在 VM Arguments 里面配置最大 DirectBuffer 和最大堆空間,代碼中分別請求了 200M 的空間,如果設置的堆空間過小,例如設置 1M,會拋出錯誤如清單 7 所示。

清單 7. 運行錯誤

Error occurred during initialization of VM
Too small initial heap for new size specified

DirectBuffer 的信息不會打印在 GC 里面,因為 GC 只記錄了堆空間的內存回收。可以看到,由于 ByteBuffer 在堆上分配空間,因此其 GC 數組相對非常頻繁,在需要頻繁創建 Buffer 的場合,由于創建和銷毀 DirectBuffer 的代碼比較高昂,不宜使用 DirectBuffer。但是如果能將 DirectBuffer 進行復用,可以大幅改善系統性能。清單 8 是一段對 DirectBuffer 進行監控代碼。

清單 8. 對 DirectBuffer 監控代碼

import java.lang.reflect.Field;

public class monDirectBuffer {

public static void main(String[] args){
try {
Class c = Class.forName("java.nio.Bits");//通過反射取得私有數據
Field maxMemory = c.getDeclaredField("maxMemory");
maxMemory.setAccessible(true);
Field reservedMemory = c.getDeclaredField("reservedMemory");
reservedMemory.setAccessible(true);
synchronized(c){
Long maxMemoryValue = (Long)maxMemory.get(null);
Long reservedMemoryValue = (Long)reservedMemory.get(null);
System.out.println("maxMemoryValue="+maxMemoryValue);
System.out.println("reservedMemoryValue="+reservedMemoryValue);
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}

運行輸出如清單 9 所示。

清單 9. 運行輸出

maxMemoryValue=67108864
reservedMemoryValue=0

由于 NIO 使用起來較為困難,所以許多公司推出了自己封裝 JDK NIO 的框架,例如 Apache 的 Mina,JBoss 的 Netty,Sun 的 Grizzly 等等,這些框架都直接封裝了傳輸層的 TCP 或 UDP 協議,其中 Netty 只是一個 NIO 框架,它不需要 Web 容器的額外支持,也就是說不限定 Web 容器。

Java AIO

AIO 相關的類和接口:

  • java.nio.channels.AsynchronousChannel:標記一個 Channel 支持異步 IO 操作;

  • java.nio.channels.AsynchronousServerSocketChannel:ServerSocket 的 AIO 版本,創建 TCP 服務端,綁定地址,監聽端口等;

  • java.nio.channels.AsynchronousSocketChannel:面向流的異步 Socket Channel,表示一個連接;

  • java.nio.channels.AsynchronousChannelGroup:異步 Channel 的分組管理,目的是為了資源共享。一個 AsynchronousChannelGroup 綁定一個線程池,這個線程池執行兩個任務:處理 IO 事件和派發 CompletionHandler。AsynchronousServerSocketChannel 創建的時候可以傳入一個 AsynchronousChannelGroup,那么通過 AsynchronousServerSocketChannel 創建的 AsynchronousSocketChannel 將同屬于一個組,共享資源;

  • java.nio.channels.CompletionHandler:異步 IO 操作結果的回調接口,用于定義在 IO 操作完成后所作的回調工作。AIO 的 API 允許兩種方式來處理異步操作的結果:返回的 Future 模式或者注冊 CompletionHandler,推薦用 CompletionHandler 的方式,這些 handler 的調用是由 AsynchronousChannelGroup 的線程池派發的。這里線程池的大小是性能的關鍵因素。

這里舉一個程序范例,簡單介紹一下 AIO 如何運作。

清單 10. 服務端程序

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutionException;

public class SimpleServer {
public SimpleServer(int port) throws IOException {
final AsynchronousServerSocketChannel listener =
AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(port));
//監聽消息,收到后啟動 Handle 處理模塊
listener.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
public void completed(AsynchronousSocketChannel ch, Void att) {
listener.accept(null, this);// 接受下一個連接
handle(ch);// 處理當前連接
}

@Override
public void failed(Throwable exc, Void attachment) {
// TODO Auto-generated method stub

}

});
}

public void handle(AsynchronousSocketChannel ch) {
ByteBuffer byteBuffer = ByteBuffer.allocate(32);//開一個 Buffer
try {
ch.read(byteBuffer).get();//讀取輸入
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
byteBuffer.flip();
System.out.println(byteBuffer.get());
// Do something
}

}

清單 11. 客戶端程序

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class SimpleClientClass {
private AsynchronousSocketChannel client;
public SimpleClientClass(String host, int port) throws IOException,
                                    InterruptedException, ExecutionException {
this.client = AsynchronousSocketChannel.open();
Future<?> future = client.connect(new InetSocketAddress(host, port));
future.get();
}

public void write(byte b) {
ByteBuffer byteBuffer = ByteBuffer.allocate(32);
System.out.println("byteBuffer="+byteBuffer);
byteBuffer.put(b);//向 buffer 寫入讀取到的字符
byteBuffer.flip();
System.out.println("byteBuffer="+byteBuffer);
client.write(byteBuffer);
}

}

清單 12.Main 函數

import java.io.IOException;
import java.util.concurrent.ExecutionException;

import org.junit.Test;

public class AIODemoTest {

@Test
public void testServer() throws IOException, InterruptedException {
SimpleServer server = new SimpleServer(9021);
Thread.sleep(10000);//由于是異步操作,所以睡眠一定時間,以免程序很快結束
}

@Test
public void testClient() throws IOException, InterruptedException, ExecutionException {
SimpleClientClass client = new SimpleClientClass("localhost", 9021);
client.write((byte) 11);
}

public static void main(String[] args){
AIODemoTest demoTest = new AIODemoTest();
try {
demoTest.testServer();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
demoTest.testClient();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}

后續會專門出文章具體深入介紹 AIO 的源代碼、設計理念、設計模式等等。

結束語

I/O 與 NIO 一個比較重要的區別是我們使用 I/O 的時候往往會引入多線程,每個連接使用一個單獨的線程,而 NIO 則是使用單線程或者只使用少量的多線程,每個連接共用一個線程。而由于 NIO 的非阻塞需要一直輪詢,比較消耗系統資源,所以異步非阻塞模式 AIO 就誕生了。本文對 I/O、NIO、AIO 等三種輸入輸出操作方式進行一一介紹,力求通過簡單的描述和實例讓讀者能夠掌握基本的操作、優化方法。

 

責任編輯:王雪燕 來源: developerworks
相關推薦

2019-02-25 08:40:28

Linux磁盤IO

2013-05-28 10:08:41

IO輸出

2009-10-10 10:10:29

服務器IO

2009-05-14 10:16:36

Oracle優化磁盤

2025-10-16 02:00:00

2024-03-05 18:24:52

I/O聚合優化存儲

2011-02-25 09:16:00

SQLSQL Server IO

2017-09-01 12:26:18

Linux調度器系統

2025-07-23 08:13:10

2009-09-29 09:50:46

2019-12-02 09:45:45

Linux IO系統

2013-09-17 15:13:28

IO

2013-09-17 13:43:51

IO

2011-12-19 14:05:01

JavaIO

2013-09-16 16:07:38

Java基礎IO

2011-07-20 14:33:19

C++IO

2022-07-28 11:09:44

Linux優化IO

2012-06-14 09:32:17

虛擬化

2011-02-22 10:37:00

SQL ServerSQL Server 性能診斷

2018-11-05 11:20:54

緩沖IO
點贊
收藏

51CTO技術棧公眾號

日干夜干天天干| 国产xxxxxxxxx| 超碰在线最新| 成人的网站免费观看| 日本久久久久久久久久久| 疯狂撞击丝袜人妻| 日韩免费电影在线观看| 欧美日韩国产综合一区二区| 屁屁影院ccyy国产第一页| 免费在线高清av| 国产成人精品影院| 国产精品美女网站| 国产奶水涨喷在线播放| 欧美亚洲在线日韩| 亚洲激情电影中文字幕| 男人的天堂最新网址| 美女高潮视频在线看| 亚洲四区在线观看| 快播亚洲色图| 性一交一乱一精一晶| 日本成人在线视频网站| 午夜精品一区二区三区在线视 | 无码内射中文字幕岛国片| 国产精品实拍| 亚洲国产精品传媒在线观看| 狠狠色狠狠色综合人人| 999久久久久久| 美女www一区二区| 欧美亚洲国产视频| 国产一级特黄视频| 欧美在线高清| 日韩中文字幕国产| 日韩免费成人av| 久久久久观看| 精品国产一二三| 伊人影院在线观看视频| 亚洲国产91视频| 欧美亚洲国产一卡| 女性隐私黄www网站视频| av资源一区| 亚洲h在线观看| 国产一二三区在线播放| 在线h片观看| 亚洲精品五月天| 91xxx视频| 成人a在线视频免费观看| 国产精品色哟哟| 日韩一本精品| av在线女优影院| 国产精品毛片大码女人| 一本色道久久综合亚洲二区三区| 久久天堂电影| 国产午夜精品一区二区三区嫩草| 日本公妇乱淫免费视频一区三区| 天天在线女人的天堂视频| 99r国产精品| 精品一区二区三区国产| 凸凹人妻人人澡人人添| 久久综合成人精品亚洲另类欧美 | 久久精品91| 国产精品pans私拍| 国产精品欧美综合| 国产在线精品不卡| 97人人干人人| 视频一区二区免费| 久久麻豆一区二区| 亚洲成人18| 黄色片网站在线观看| 亚洲精品高清在线观看| 性高湖久久久久久久久aaaaa| 国产黄色在线观看| 亚洲一区二区3| 成人免费观看cn| 羞羞影院欧美| 91精品在线观看入口| 国产精品99精品无码视亚| 欧美性生活一级片| 一区二区三区黄色| 放荡的美妇在线播放| 亚洲国产日韩欧美一区二区三区| 57pao国产精品一区| 色婷婷久久综合中文久久蜜桃av| 久久精品国产99久久6| 2014亚洲精品| 日韩av资源| 综合久久久久久| 男人添女人下面高潮视频| 福利一区视频| 精品处破学生在线二十三| 一道本在线观看| 五月婷婷亚洲| 97香蕉超级碰碰久久免费的优势| 黄瓜视频在线免费观看| 久久成人18免费观看| 国产精品成人观看视频免费| 精品三级久久久久久久电影聊斋| 亚洲欧美另类小说视频| 99福利在线观看| 日本精品视频| 亚洲一二在线观看| 国产真实夫妇交换视频| 久久99深爱久久99精品| 另类欧美小说| 欧美xxxx黑人又粗又长| 欧洲生活片亚洲生活在线观看| 丰满少妇中文字幕| av资源久久| 午夜精品久久久99热福利| 国产美女裸体无遮挡免费视频| 99久久婷婷国产综合精品| 手机在线视频你懂的| 欧美aa在线观看| 精品久久一区二区三区| 99热在线观看精品| 丝袜美腿亚洲一区| 精选一区二区三区四区五区| 18视频在线观看网站| 在线观看av一区二区| 在线观看国产免费视频| 欧美激情四色| 成人黄色免费片| 成人精品一区二区三区免费 | 最近免费中文字幕大全免费版视频| 国产精品一区免费视频| 亚洲图片欧洲图片日韩av| 成人动漫一区| 日韩精品久久久久久福利| 九九九在线视频| 国产揄拍国内精品对白| 亚洲精品免费在线看| 成人免费福利| 亚洲男女自偷自拍图片另类| 97超碰人人干| 99久久夜色精品国产网站| 国产av熟女一区二区三区| 久久av网站| 欧美乱妇高清无乱码| 91精品在线视频观看| 中国色在线观看另类| 亚洲成人av免费看| 精品日韩毛片| 国产三级精品网站| 理论片午午伦夜理片在线播放| 欧洲亚洲精品在线| 日本人亚洲人jjzzjjz| 日韩激情一二三区| 午夜精品一区二区在线观看的 | 成人动漫视频在线观看完整版| 污污网站在线看| 欧美xfplay| 国产精品第72页| 成人av网站大全| 国产欧美在线一区| 欧美理论视频| 国产精品一区二区久久精品| 蜜桃视频网站在线观看| 欧美一区二视频| 欧美久久久久久久久久久久| 成人自拍视频在线观看| 无码aⅴ精品一区二区三区浪潮 | 噼里啪啦国语在线观看免费版高清版| 天堂av一区二区三区在线播放| 日本电影亚洲天堂| 亚洲欧美视频一区二区| 日韩一级欧美一级| 国产午夜激情视频| 国产丝袜在线精品| 在线能看的av网站| 国内精品99| 欧美一级爽aaaaa大片| 97欧美成人| 久久成人免费视频| 天堂中文在线资源| 欧美四级电影网| 欧美日韩中文字幕在线观看 | 精品少妇一区二区三区在线播放| 日本一区二区不卡在线| 国产女人水真多18毛片18精品视频 | 5858s免费视频成人| 久久黄色免费视频| 久久久亚洲综合| 日韩高清在线一区二区| 亚洲作爱视频| 伊人精品久久久久7777| 六月丁香久久丫| 国产日韩在线精品av| 激情av在线播放| 一个色综合导航| 亚洲精品久久久久久无码色欲四季 | 国产又黄又粗又猛又爽| 午夜av一区二区三区| 国产三级精品三级观看| 99久久99久久综合| 天天做天天干天天操| 国产日韩高清一区二区三区在线| 在线视频不卡国产| 亚洲人成网亚洲欧洲无码| 国产精品视频26uuu| 免费电影网站在线视频观看福利| 亚洲欧美成人一区二区在线电影| 国产又粗又猛又爽又黄91| 欧美性xxxx在线播放| 日本妇女毛茸茸| 国产日韩欧美高清| 69亚洲乱人伦| 久久99国内精品| 337p粉嫩大胆噜噜噜鲁| 欧美激情91| 亚洲图片小说在线| 欧美美乳视频| 国产精品久久亚洲7777| 综合久草视频| 国产精品白嫩初高中害羞小美女| 精品日韩av| 久久99精品视频一区97| av天在线观看| 一区二区三区动漫| 日韩一二三四| 亚洲精品国产精品乱码不99按摩| 国产理论片在线观看| 欧美日韩免费视频| 久久永久免费视频| 欧美色播在线播放| 日韩成人一区二区三区| 自拍偷拍国产精品| 中文乱码字幕高清一区二区| 国产亚洲自拍一区| 中文字幕一区二区人妻在线不卡| 国产91在线观看| 日韩欧美中文视频| 另类成人小视频在线| 日日躁夜夜躁aaaabbbb| 日本伊人色综合网| 中文字幕在线观看第三页| 国产欧美日韩一区二区三区在线| 欧美国产视频一区| 国色天香一区二区| 国产www免费| 亚洲国产清纯| 精品无码一区二区三区爱欲| 国产精品mm| 国内少妇毛片视频| 激情亚洲网站| 成人免费播放器| aa国产精品| www.色就是色| 久久精品99久久久| 日韩av影视大全| 成人精品亚洲人成在线| 国产污在线观看| 91亚洲永久精品| xxx在线播放| 国产精品久久网站| 五月天丁香激情| 亚洲一区二区三区四区不卡| 国产在线拍揄自揄拍| 亚洲电影一区二区| 男人天堂av在线播放| 欧美三级乱人伦电影| 国产情侣在线播放| 精品日韩一区二区三区免费视频| 色哟哟中文字幕| 亚洲欧洲在线视频| 日韩av中文| 欧美精品福利在线| 中文字幕资源网在线观看免费| 国产成人精品视频在线观看| 国产成人免费精品| 91久久久精品| 日韩成人午夜| 亚洲人体一区| 黄色精品免费| 欧美伦理视频在线观看| 国产在线国偷精品免费看| 深夜视频在线观看| 久久精品在线观看| 成人免费毛片东京热| 午夜国产精品一区| 在线视频 91| 精品欧美乱码久久久久久1区2区| 日韩专区一区二区| 久久影院中文字幕| 亚洲同志男男gay1069网站| 国产精品一二三在线| 911精品国产| 五月天丁香综合久久国产 | 亚洲精品一区二区三区不卡| 日韩成人在线免费观看| 免费av不卡| 18一19gay欧美视频网站| 国产亚洲人成a在线v网站| 99视频在线| 欧美一级精品| 国产午夜福利在线播放| 激情深爱一区二区| 四虎国产精品成人免费入口| 亚洲猫色日本管| 国产成人精品一区二区色戒| 精品福利av导航| 国产成人无吗| 国产精品毛片a∨一区二区三区|国 | 91国偷自产一区二区使用方法| 精品久久久久久亚洲综合网站| 国产视频久久网| 国产网红在线观看| 国产一区在线播放| 蜜桃a∨噜噜一区二区三区| a级网站在线观看| 蜜桃视频第一区免费观看| 污污污www精品国产网站| 亚洲视频在线一区| 最好看的日本字幕mv视频大全| 亚洲国产一区二区三区四区| 日韩专区av| 91九色国产视频| 日韩情爱电影在线观看| 国产xxxxx在线观看| 大白屁股一区二区视频| 欧美日韩中文字幕在线观看| 欧美高清视频在线高清观看mv色露露十八| 视频在线不卡| 欧美最顶级丰满的aⅴ艳星| 开心激情综合| 人妻夜夜添夜夜无码av| 丰满放荡岳乱妇91ww| 天天色影综合网| 在线观看91av| caoporn免费在线视频| 成人欧美一区二区三区在线| 波多野结衣在线播放一区| 韩国中文字幕av| 中文在线一区二区 | 伊人成综合网站| 久久精品日产第一区二区三区乱码 | 国产a一区二区| 国产一区二区三区四区老人| 亚洲av无一区二区三区久久| 亚洲欧美一区二区久久| 99国产在线播放| 欧美xxxx18国产| 日韩在线网址| 无码人妻精品一区二区蜜桃网站| 国产福利一区二区| 国产在线观看免费视频今夜| 亚洲成av人乱码色午夜| av成人福利| 欧美精品久久久| 日韩黄色免费电影| 手机免费观看av| 69堂亚洲精品首页| 国内高清免费在线视频| 国产伦精品一区二区三区在线 | 日韩精品四区| 日韩成人精品视频在线观看| 亚洲日本电影在线| 性生活视频软件| 欧美一区二区三区精品电影| 国产亚洲欧美日韩在线观看一区二区 | 国产亚洲精品久久久久久无几年桃| 精品欧美乱码久久久久久| 人在线成免费视频| 日韩一区二区三区高清| 精品一区二区三区在线播放 | 国外成人免费视频| 久久久久久久尹人综合网亚洲| 免费在线观看a视频| 91麻豆精品国产91久久久久久 | 欧美日韩一视频区二区| 国产成人av无码精品| 欧美性色黄大片手机版| 日本一级理论片在线大全| 久久er99热精品一区二区三区| 日韩精品成人一区二区三区| 看免费黄色录像| 亚洲国产一区二区三区在线观看| 日本在线中文字幕一区二区三区 | 欧洲在线视频| 欧美二区在线| 激情久久久久久久久久久久久久久久| 美女视频黄免费| 亚洲色图狂野欧美| 波多野结衣在线一区二区| 国产熟人av一二三区| 亚洲国产va精品久久久不卡综合| 久久久久久女乱国产| www日韩av| 奇米精品一区二区三区四区| 久久久久久欧美精品se一二三四| 亚洲网站在线播放| 盗摄牛牛av影视一区二区| 亚洲精品一二三四五区| 亚洲成av人片一区二区三区| 日本精品一区二区三区在线播放| 久久久精品动漫| 国产精一品亚洲二区在线视频| 波多野结衣在线电影| 欧美另类69精品久久久久9999| 青青草成人影院| jizz欧美性20| 亚洲电影在线看|