亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java I/O 操作及優(yōu)化詳細(xì)介紹

 更新時(shí)間:2017年02月13日 09:22:40   投稿:lqh  
這篇文章主要介紹了Java I/O 操作及優(yōu)化詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下

概要:

流是一組有順序的,有起點(diǎn)和終點(diǎn)的字節(jié)集合,是對(duì)數(shù)據(jù)傳輸?shù)目偡Q或抽象。即數(shù)據(jù)在兩設(shè)備間的傳輸稱為流,流的本質(zhì)是數(shù)據(jù)傳輸,根據(jù)數(shù)據(jù)傳輸特性將流抽象為各種類,方便更直觀的進(jìn)行數(shù)據(jù)操作。

Java I/O

I/O,即 Input/Output(輸入/輸出) 的簡(jiǎn)稱。就 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)對(duì)上述模型支持不同,UNIX 支持 IO 多路復(fù)用。不同系統(tǒng)叫法不同,freebsd 里面叫 kqueue,Linux 叫 epoll。而 Windows2000 的時(shí)候就誕生了 IOCP 用以支持 asynchronous I/O。

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

Java I/O 的相關(guān)方法如下所述:

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

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

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

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

Java NIO

Java.nio 包是 Java 在 1.4 版本之后新增加的包,專門(mén)用來(lái)提高 I/O 操作的效率。

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

表 1. I/O VS NIO

I/O NIO
面向流 面向緩沖
阻塞 IO 非阻塞 IO
無(wú) 選擇器

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

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

  1. 寫(xiě)入數(shù)據(jù)到 Buffer;
  2. 調(diào)用 flip() 方法;
  3. 從 Buffer 中讀取數(shù)據(jù);
  4. 調(diào)用 clear() 方法或者 compact() 方法。

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

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

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

圖 1 Buffer 接口層次圖

 

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

  1. 從 Channel 寫(xiě)到 Buffer,如例子中 Channel 從文件中讀取數(shù)據(jù),寫(xiě)到 Channel;
  2. 直接調(diào)用 put 方法,往里面寫(xiě)數(shù)據(jù)。

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

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

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

clear() 和 compact() 方法

一旦讀完 Buffer 中的數(shù)據(jù),需要讓 Buffer 準(zhǔn)備好再次被寫(xiě)入??梢酝ㄟ^(guò) clear() 或 compact() 方法來(lái)完成。

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

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

Buffer 參數(shù)

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

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

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

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

散射&聚集

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

清單 1. 使用散射和聚集讀寫(xiě)結(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. 運(yùn)行結(jié)果

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

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

清單 3. I/O 的三種方式對(duì)比試驗(yàn)

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);//寫(xiě)入 4000000 個(gè)整數(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();//準(zhǔn)備寫(xiě)
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();//準(zhǔn)備讀取數(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));//最高位,無(wú)符號(hào)右移
 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 運(yùn)行輸出如清單 4 所示。

清單 4. 運(yùn)行輸出

1139
906
296
157
234
125

除上述描述及清單 3 所示代碼以外,NIO 的 Buffer 還提供了一個(gè)可以直接訪問(wèn)系統(tǒng)物理內(nèi)存的類 DirectBuffer。DirectBuffer 繼承自 ByteBuffer,但和普通的 ByteBuffer 不同。普通的 ByteBuffer 仍然在 JVM 堆上分配空間,其最大內(nèi)存受到最大堆的限制,而 DirectBuffer 直接分配在物理內(nèi)存上,并不占用堆空間。在對(duì)普通的 ByteBuffer 訪問(wèn)時(shí),系統(tǒng)總是會(huì)使用一個(gè)“內(nèi)核緩沖區(qū)”進(jìn)行間接的操作。而 DirectrBuffer 所處的位置,相當(dāng)于這個(gè)“內(nèi)核緩沖區(qū)”。因此,使用 DirectBuffer 是一種更加接近系統(tǒng)底層的方法,所以,它的速度比普通的 ByteBuffer 更快。DirectBuffer 相對(duì)于 ByteBuffer 而言,讀寫(xiě)訪問(wèn)速度快很多,但是創(chuàng)建和銷毀 DirectrBuffer 的花費(fèi)卻比 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();
 }
}

運(yùn)行輸出如清單 6 所示。

清單 6. 運(yùn)行輸出

920
110
531
390

由清單 6 可知,頻繁創(chuàng)建和銷毀 DirectBuffer 的代價(jià)遠(yuǎn)遠(yuǎn)大于在堆上分配內(nèi)存空間。使用參數(shù)-XX:MaxDirectMemorySize=200M –Xmx200M 在 VM Arguments 里面配置最大 DirectBuffer 和最大堆空間,代碼中分別請(qǐng)求了 200M 的空間,如果設(shè)置的堆空間過(guò)小,例如設(shè)置 1M,會(huì)拋出錯(cuò)誤如清單 7 所示。

清單 7. 運(yùn)行錯(cuò)誤

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

DirectBuffer 的信息不會(huì)打印在 GC 里面,因?yàn)?GC 只記錄了堆空間的內(nèi)存回收??梢钥吹?,由于 ByteBuffer 在堆上分配空間,因此其 GC 數(shù)組相對(duì)非常頻繁,在需要頻繁創(chuàng)建 Buffer 的場(chǎng)合,由于創(chuàng)建和銷毀 DirectBuffer 的代碼比較高昂,不宜使用 DirectBuffer。但是如果能將 DirectBuffer 進(jìn)行復(fù)用,可以大幅改善系統(tǒng)性能。清單 8 是一段對(duì) DirectBuffer 進(jìn)行監(jiān)控代碼。

清單 8. 對(duì) 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");//通過(guò)反射取得私有數(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();
}

}
}

運(yùn)行輸出如清單 9 所示。

清單 9. 運(yùn)行輸出

maxMemoryValue=67108864
reservedMemoryValue=0

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

Java AIO

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

java.nio.channels.AsynchronousChannel:標(biāo)記一個(gè) Channel 支持異步 IO 操作;
java.nio.channels.AsynchronousServerSocketChannel:ServerSocket 的 AIO 版本,創(chuàng)建 TCP 服務(wù)端,綁定地址,監(jiān)聽(tīng)端口等;
java.nio.channels.AsynchronousSocketChannel:面向流的異步 Socket Channel,表示一個(gè)連接;
java.nio.channels.AsynchronousChannelGroup:異步 Channel 的分組管理,目的是為了資源共享。一個(gè) AsynchronousChannelGroup 綁定一個(gè)線程池,這個(gè)線程池執(zhí)行兩個(gè)任務(wù):處理 IO 事件和派發(fā) CompletionHandler。AsynchronousServerSocketChannel 創(chuàng)建的時(shí)候可以傳入一個(gè) AsynchronousChannelGroup,那么通過(guò) AsynchronousServerSocketChannel 創(chuàng)建的 AsynchronousSocketChannel 將同屬于一個(gè)組,共享資源;
java.nio.channels.CompletionHandler:異步 IO 操作結(jié)果的回調(diào)接口,用于定義在 IO 操作完成后所作的回調(diào)工作。AIO 的 API 允許兩種方式來(lái)處理異步操作的結(jié)果:返回的 Future 模式或者注冊(cè) CompletionHandler,推薦用 CompletionHandler 的方式,這些 handler 的調(diào)用是由 AsynchronousChannelGroup 的線程池派發(fā)的。這里線程池的大小是性能的關(guān)鍵因素。

這里舉一個(gè)程序范例,簡(jiǎn)單介紹一下 AIO 如何運(yùn)作。

清單 10. 服務(wù)端程序

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)聽(tīng)消息,收到后啟動(dòng) Handle 處理模塊
listener.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
public void completed(AsynchronousSocketChannel ch, Void att) { 
listener.accept(null, this);// 接受下一個(gè)連接 
handle(ch);// 處理當(dāng)前連接 
}

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

} 

});
}

public void handle(AsynchronousSocketChannel ch) { 
ByteBuffer byteBuffer = ByteBuffer.allocate(32);//開(kāi)一個(gè) 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 寫(xiě)入讀取到的字符 
 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);//由于是異步操作,所以睡眠一定時(shí)間,以免程序很快結(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ù)會(huì)專門(mén)出文章具體深入介紹 AIO 的源代碼、設(shè)計(jì)理念、設(shè)計(jì)模式等等。

結(jié)束語(yǔ)

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

 感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!

相關(guān)文章

  • java調(diào)用Hbase報(bào)錯(cuò)解決方法

    java調(diào)用Hbase報(bào)錯(cuò)解決方法

    這篇文章主要為大家介紹了java調(diào)用Hbase報(bào)錯(cuò)解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • 在IDEA中配置Maven鏡像的最新方法

    在IDEA中配置Maven鏡像的最新方法

    這篇文章主要給大家介紹了關(guān)于在IDEA中配置Maven鏡像的最新方法,Maven是一個(gè)流行的Java項(xiàng)目構(gòu)建工具,它依賴于互聯(lián)網(wǎng)上的Mave中央倉(cāng)庫(kù)來(lái)下載和管理項(xiàng)目依賴庫(kù),文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-11-11
  • 70行Java代碼實(shí)現(xiàn)深度神經(jīng)網(wǎng)絡(luò)算法分享

    70行Java代碼實(shí)現(xiàn)深度神經(jīng)網(wǎng)絡(luò)算法分享

    這篇文章主要介紹了70行Java代碼實(shí)現(xiàn)深度神經(jīng)網(wǎng)絡(luò)算法分享,涉及神經(jīng)網(wǎng)絡(luò)的計(jì)算過(guò)程,神經(jīng)網(wǎng)絡(luò)的算法程序?qū)崿F(xiàn),多層神經(jīng)網(wǎng)絡(luò)完整程序?qū)崿F(xiàn)等相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以參考下。
    2017-11-11
  • 詳解springmvc常用5種注解

    詳解springmvc常用5種注解

    在本篇里我們給大家總結(jié)了關(guān)于springmvc常用5種注解相關(guān)知識(shí)點(diǎn)以及實(shí)例代碼,需要的朋友們參考下。
    2019-07-07
  • java對(duì)象中什么時(shí)候適合用static修飾符踩坑解決記錄

    java對(duì)象中什么時(shí)候適合用static修飾符踩坑解決記錄

    這篇文章主要為大家介紹了java對(duì)象中什么時(shí)候適合用static修飾符踩坑解決記錄,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • MyBatis通用的10種寫(xiě)法總結(jié)大全

    MyBatis通用的10種寫(xiě)法總結(jié)大全

    這篇文章主要給大家介紹了關(guān)于MyBatis通用的10種寫(xiě)法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-11-11
  • Java數(shù)據(jù)結(jié)構(gòu)與算法實(shí)現(xiàn)遞歸與回溯

    Java數(shù)據(jù)結(jié)構(gòu)與算法實(shí)現(xiàn)遞歸與回溯

    本文主要介紹了Java數(shù)據(jù)結(jié)構(gòu)與算法實(shí)現(xiàn)遞歸與回溯,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • java用兩個(gè)例子充分闡述多態(tài)的可拓展性介紹

    java用兩個(gè)例子充分闡述多態(tài)的可拓展性介紹

    下面小編就為大家?guī)?lái)一篇java用兩個(gè)例子充分闡述多態(tài)的可拓展性介紹。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-06-06
  • java?中如何實(shí)現(xiàn)?List?集合去重

    java?中如何實(shí)現(xiàn)?List?集合去重

    這篇文章主要介紹了java?中如何實(shí)現(xiàn)?List?集合去重,List?去重指的是將?List?中的重復(fù)元素刪除掉的過(guò)程,下文操作操作過(guò)程介紹需要的小伙伴可以參考一下
    2022-05-05
  • SpringBoot實(shí)現(xiàn)定時(shí)發(fā)送郵件的三種方法案例詳解

    SpringBoot實(shí)現(xiàn)定時(shí)發(fā)送郵件的三種方法案例詳解

    這篇文章主要介紹了SpringBoot三種方法實(shí)現(xiàn)定時(shí)發(fā)送郵件的案例,Spring框架的定時(shí)任務(wù)調(diào)度功能支持配置和注解兩種方式Spring?Boot在Spring框架的基礎(chǔ)上實(shí)現(xiàn)了繼承,并對(duì)其中基于注解方式的定時(shí)任務(wù)實(shí)現(xiàn)了非常好的支持,本文給大家詳細(xì)講解,需要的朋友可以參考下
    2023-03-03

最新評(píng)論