自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java I/O 操作及優(yōu)化建議

開發(fā) 后端
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)。不同的操作系統(tǒng)對上述模型支持不同,UNIX 支持 IO 多路復用。不同系統(tǒng)叫法不同,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)。不同的操作系統(tǒng)對上述模型支持不同,UNIX 支持 IO 多路復用。不同系統(tǒng)叫法不同,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 的相關(guān)方法如下所述:

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

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

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

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

Java NIO

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

表 1 所示是 I/O 與 NIO 之間的對比內(nèi)容。

表 1. I/O VS NIO

I/O

NIO

面向流

面向緩沖

阻塞 IO

非阻塞 IO

選擇器

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

使用 Buffer 讀寫數(shù)據(jù)一般遵循以下四個步驟:

  1. 寫入數(shù)據(jù)到 Buffer;

  2. 調(diào)用 flip() 方法;

  3. 從 Buffer 中讀取數(shù)據(jù);

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

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

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

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

圖 1 Buffer 接口層次圖

Java I/O 操作及優(yōu)化建議

Buffer 寫數(shù)據(jù)有兩種情況:

  1. 從 Channel 寫到 Buffer,如例子中 Channel 從文件中讀取數(shù)據(jù),寫到 Channel;

  2. 直接調(diào)用 put 方法,往里面寫數(shù)據(jù)。

從 Buffer 中讀取數(shù)據(jù)有兩種方式:

  1. 從 Buffer 讀取數(shù)據(jù)到 Channel;

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

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

clear() 和 compact() 方法

一旦讀完 Buffer 中的數(shù)據(jù),需要讓 Buffer 準備好再次被寫入??梢酝ㄟ^ clear() 或 compact() 方法來完成。

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

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

Buffer 參數(shù)

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

capacity 是指 Buffer 的大小,在 Buffer 建立的時候已經(jīng)確定。

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

position 當 Buffer 處于寫模式,指下一個寫數(shù)據(jù)的位置;處于讀模式,當前將要讀取的數(shù)據(jù)的位置。每讀寫一個數(shù)據(jù),position+1,也就是 limit 和 position 在 Buffer 的讀/寫時的含義不一樣。當調(diào)用 Buffer 的 flip 方法,由寫模式變?yōu)樽x模式時,limit(讀)=position(寫),position(讀) =0。

散射&聚集

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

清單 1. 使用散射和聚集讀寫結(jié)構(gòu)化文件

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 性能優(yōu)化技巧".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. 運行結(jié)果

java 性能優(yōu)化技巧 test

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

清單 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 個整數(shù)
}
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));//將整數(shù)轉(zhuǎn)為數(shù)組
}
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);//讀取文件數(shù)據(jù)
fc.close();
byteBuffer.flip();//準備讀取數(shù)據(jù)
while(byteBuffer.hasRemaining()){
byte2int(byteBuffer.get(),byteBuffer.get(),byteBuffer.get(),byteBuffer.get());//將 byte 轉(zhuǎn)為整數(shù)
}
} 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();
//將文件直接映射到內(nèi)存的方法
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 還提供了一個可以直接訪問系統(tǒng)物理內(nèi)存的類 DirectBuffer。DirectBuffer 繼承自 ByteBuffer,但和普通的 ByteBuffer 不同。普通的 ByteBuffer 仍然在 JVM 堆上分配空間,其最大內(nèi)存受到最大堆的限制,而 DirectBuffer 直接分配在物理內(nèi)存上,并不占用堆空間。在對普通的 ByteBuffer 訪問時,系統(tǒng)總是會使用一個“內(nèi)核緩沖區(qū)”進行間接的操作。而 DirectrBuffer 所處的位置,相當于這個“內(nèi)核緩沖區(qū)”。因此,使用 DirectBuffer 是一種更加接近系統(tǒng)底層的方法,所以,它的速度比普通的 ByteBuffer 更快。DirectBuffer 相對于 ByteBuffer 而言,讀寫訪問速度快很多,但是創(chuàng)建和銷毀 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);//創(chuàng)建 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);//創(chuàng)建 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 可知,頻繁創(chuàng)建和銷毀 DirectBuffer 的代價遠遠大于在堆上分配內(nèi)存空間。使用參數(shù)-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 只記錄了堆空間的內(nèi)存回收??梢钥吹剑捎?ByteBuffer 在堆上分配空間,因此其 GC 數(shù)組相對非常頻繁,在需要頻繁創(chuàng)建 Buffer 的場合,由于創(chuàng)建和銷毀 DirectBuffer 的代碼比較高昂,不宜使用 DirectBuffer。但是如果能將 DirectBuffer 進行復用,可以大幅改善系統(tǒng)性能。清單 8 是一段對 DirectBuffer 進行監(jiān)控代碼。

清單 8. 對 DirectBuffer 監(jiān)控代碼

import java.lang.reflect.Field;

public class monDirectBuffer {

public static void main(String[] args){
try {
Class c = Class.forName("java.nio.Bits");//通過反射取得私有數(shù)據(jù)
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 協(xié)議,其中 Netty 只是一個 NIO 框架,它不需要 Web 容器的額外支持,也就是說不限定 Web 容器。

Java AIO

AIO 相關(guān)的類和接口:

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

  • java.nio.channels.AsynchronousServerSocketChannel:ServerSocket 的 AIO 版本,創(chuàng)建 TCP 服務端,綁定地址,監(jiān)聽端口等;

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

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

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

這里舉一個程序范例,簡單介紹一下 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));
//監(jiān)聽消息,收到后啟動 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 函數(shù)

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);//由于是異步操作,所以睡眠一定時間,以免程序很快結(jié)束
}

@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();
}
}

}

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

結(jié)束語

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

 

責任編輯:王雪燕 來源: developerworks
相關(guān)推薦

2019-02-25 08:40:28

Linux磁盤IO

2013-05-28 10:08:41

IO輸出

2024-03-05 18:24:52

I/O聚合優(yōu)化存儲

2009-05-14 10:16:36

Oracle優(yōu)化磁盤

2009-10-10 10:10:29

服務器IO

2011-02-25 09:16:00

SQLSQL Server IO

2019-12-02 09:45:45

Linux IO系統(tǒng)

2009-09-29 09:50:46

2017-09-01 12:26:18

Linux調(diào)度器系統(tǒng)

2013-09-16 16:07:38

Java基礎(chǔ)IO

2011-07-20 14:33:19

C++IO

2011-12-19 14:05:01

JavaIO

2013-09-17 13:43:51

IO

2013-09-17 15:13:28

IO

2012-06-14 09:32:17

虛擬化

2022-07-28 11:09:44

Linux優(yōu)化IO

2011-02-22 10:37:00

SQL ServerSQL Server 性能診斷

2018-11-05 11:20:54

緩沖IO

2024-10-17 16:47:05

磁盤I/O計算機

2010-03-08 09:17:13

F#異步
點贊
收藏

51CTO技術(shù)棧公眾號