Java NIO入門到精通
NIO(New I/O)系統,即Java的第二個I/O系統。NIO提供了與標準I/O API不同的I/O處理方式。它是Java用來替代傳統I/O API(Java 1.4)。它支援面向緩衝的,基於通道的I/O操作方法。隨著JDK 7的推出,NIO系統得到了擴充套件,為檔案系統功能和檔案處理提供了增強的支援。 由於NIO檔案類支援的這些新的功能,NIO被廣泛應用於檔案處理。
NIO為Java程式設計師實現高速I/O,而不使用自定義本機程式碼。 NIO將填充,排放緩衝區等的時間性I/O活動移回作業系統,從而大大提高了操作速度。
NIO類包含在一個叫作java.nio包的包中。基於流的I/O是在java.io包中,為了更好地學習NIO中的知識內容,下面看下IO和NIO區別:
IO | NIO |
---|---|
基於阻塞I/O操作 | 基於非阻塞I/O操作 |
面向流的 | 面向快取的 |
通道不可用 | 通道可用於非阻塞I/O操作 |
選擇器不可用 | 選擇器可用於非阻塞I/O操作 |
阻塞I/O 阻塞IO等待資料寫入或返回前的讀取。Java IO的各種流是阻塞的。這意味著當執行緒呼叫write()或read()時,執行緒會被阻塞,直到有一些資料可用於讀取或資料被完全寫入。
非阻塞I/O 非阻塞IO不等待返回前讀取或寫入資料。 Java NIO非阻塞模式允許執行緒請求向通道寫入資料,但不等待它被完全寫入。允許執行緒繼續進行,並做其他事情。
面向流 Java IO是面向流的I/O,這意味著我們需要從流中讀取一個或多個位元組。它使用流來在資料來源/槽和java程式之間傳輸資料。使用此方法的I/O操作較慢。
面向緩衝
通道和流之間的主要區別是:流可以用於單向資料傳輸,通道提供雙向資料傳輸。因此,通過在java NIO中引入通道,可以執行非阻塞I/O操作。
通道 在Java NIO中,通道是在實體和位元組緩衝區之間有效傳輸資料的媒介。它從一個實體讀取資料,並將其放在緩衝區塊中以供消費。通道作為Java NIO提供的閘道器來訪問I/O機制。通常,通道與作業系統檔案描述符具有一對一關係,用於提供平臺獨立操作功能。通道使用原生代碼執行實際工作。通道介面允許我們以便攜和受控的方式訪問低階I/O服務。
選擇器 在Java NIO中,選擇器是可選擇通道的多路複用器,可用作可以進入非阻塞模式的特殊型別的通道。它可以檢查一個或多個NIO通道,並確定哪個通道準備好進行通訊,即讀取或寫入。選擇器用於使用單個執行緒處理多個通道。因此,它需要較少的執行緒來處理這些通道。執行緒之間的切換對於作業系統來說是昂貴的。 因此,為了提高系統效率選擇器是有用的。
Java NIO基本元件如下:
- 通道和緩衝區(Channels and Buffers):在標準I/O API中,使用字元流和位元組流。 在NIO中,使用通道和緩衝區。資料總是從緩衝區寫入通道,並從通道讀取到緩衝區。
- 選擇器(Selectors):Java NIO提供了“選擇器”的概念。這是一個可以用於監視多個通道的物件,如資料到達,連線開啟等。因此,單執行緒可以監視多個通道中的資料。
- 非阻塞I/O(Non-blocking I/O):Java NIO提供非阻塞I/O的功能。這裡應用程式立即返回任何可用的資料,應用程式應該具有池化機制,以查明是否有更多資料準備就緒。
在Java中,NIO讀寫是I/O的基本過程。從通道讀取:建立一個緩衝區,然後請求通道讀取資料。通道寫入:建立一個緩衝區,填充資料,並要求通道寫入資料。
讀寫操作中使用的核心部件有:Channels,Buffers 和 Selectors。Java NIO還有其它更多的元件和類,但是Channel,Buffer和Selector用作API的核心。
在標準I/O API中,使用字元流和位元組流。 在NIO中使用通道和緩衝區。 NIO中的所有I/O都是通過一個通道開始的。資料總是從緩衝區寫入通道,並從通道讀取到緩衝區。
在Java NIO中,主要使用的通道如下:DatagramChannel,SocketChannel,FileChannel 和 ServerSocketChannel。上述通道涵蓋UDP(使用者資料報協議)+ TCP(傳輸控制協議)網路I/O和檔案I/O。
DatagramChannel:資料報通道可以通過UDP(使用者資料報協議)通過網路讀取和寫入資料。它使用工廠方法來建立新物件。
開啟和關閉DatagramChannel的語法:
DatagramChannel ch = DatagramChannel.open();
DatagramChannel ch = DatagramChannel.close();
SocketChannel:資料報通道可以通過TCP(傳輸控制協議)通過網路讀取和寫入資料。它還使用工廠方法來建立新物件。
開啟和關閉SocketChannel的語法:
SocketChannel ch = SocketChannel.open();
ch.connect(new InetSocketAddress(host, port));
SocketChannel ch = SocketChannel.close();
ch.connect(new InetSocketAddress(host, port));
Java NIO SocketChannel用於將通道與TCP(傳輸控制協議)網路套接字連線。它相當於網路程式設計中使用的Java網路套接字(Socket)。
FileChannel:檔案通道用於從檔案讀取資料。它只能通過呼叫getChannel()方法來建立物件。不能直接建立FileChannel物件。
public class ChannelDemo {
public static void main(String args[]) throws IOException {
ReadableByteChannel source = new FileInputStream("article.json").getChannel();
WritableByteChannel destination = new FileOutputStream("article2.json").getChannel();
copyData(source, destination);
source.close();
destination.close();
}
private static void copyData(ReadableByteChannel src, WritableByteChannel dest) throws IOException {
ByteBuffer buffer = ByteBuffer.allocateDirect(20 * 1024);
while (src.read(buffer) != -1) {
buffer.flip();
while (buffer.hasRemaining()) {
dest.write(buffer);
}
buffer.clear();
}
}
}
ServerSocketChannel:ServerSocketChannel允許使用者監聽傳入的TCP連線,與Web伺服器相同。對於每個傳入連線,都會為連線建立一個SocketChannel。
ServerSocketChannel ch = ServerSocketChannel.open();
ch.socket().bind (new InetSocketAddress (port));
Java NIO ServerSocketChannel還可以用來將通道與TCP(傳輸控制協議)網路套接字連線起來。它相當於網路程式設計中使用的Java網路套接字。ServerSocketChannel類位於java.nio.channels包中。
在Java NIO中,通道是用於在實體和位元組緩衝區之間有效傳輸資料的介質。它從一個實體讀取資料,並將其放在緩衝區塊中以供消費。通道作為Java NIO提供的閘道器來訪問I/O機制。通常,通道與作業系統檔案描述符具有一對一關係,用於提供平臺獨立操作功能。
在Java NIO中,通道提供了稱為分散/聚集或向量I/O的重要功能。 這是一種簡單但功能強大的技術,通過這種技術,使用單個write()函式將位元組從一組緩衝區寫入流,並且可以使用單個read()函式將位元組從流讀取到一組緩衝區中。
“分散讀取”用於將資料從單個通道讀取多個緩衝區中的資料。
“聚集寫入”用於將資料從多個緩衝區寫入單個通道。
public class ScatterGatherDemo {
private static Charset cs = Charset.forName("UTF-8");
public static void main(String params[]) {
String str = "Scattering and Gathering Demo from Angelia";
gatherBytes(str);
scatterBytes();
}
public static void gatherBytes(String data) {
ByteBuffer buffer1 = ByteBuffer.allocate(8);
buffer1 = cs.encode("88888888");
ByteBuffer buffer2 = ByteBuffer.allocate(400);
buffer2 = cs.encode(data);
try {
//建立 GatheringByteChannel, 資料寫到檔案
createChannelInstance("scattergather.txt", true).write(new ByteBuffer[] { buffer1, buffer2 });
} catch (Exception e) {
e.printStackTrace();
}
}
public static void scatterBytes() {
ByteBuffer buffer1 = ByteBuffer.allocate(8);
ByteBuffer buffer2 = ByteBuffer.allocate(400);
try {
createChannelInstance("scattergather.txt", false).read(new ByteBuffer[] { buffer1, buffer2 });
} catch (Exception e) {
e.printStackTrace();
}
buffer1.rewind();
buffer2.rewind();
System.out.println(cs.decode(buffer1).toString());
System.out.println(cs.decode(buffer2).toString());
}
public static FileChannel createChannelInstance(String file, boolean isOutput) {
FileChannel FChannel = null;
try {
if (isOutput) {
FChannel = new FileOutputStream(file).getChannel();
} else {
FChannel = new FileInputStream(file).getChannel();
}
} catch (Exception e) {
e.printStackTrace();
}
return FChannel;
}
}
在Java NIO中,可以非常頻繁地將資料從一個通道傳輸到另一個通道。批量傳輸檔案資料是非常普遍的,因為幾個優化方法已經新增到FileChannel類中,使其更有效率。
通道之間的資料傳輸在FileChannel類中的兩種方法是:
FileChannel.transferTo()方法
FileChannel.transferFrom()方法
public class TransferDemo {
public static void main(String[] argv) throws Exception {
String[] inF = new String[] { "article.json", "article2.json" };
FileOutputStream output = new FileOutputStream(new File("combine_article.txt"));
WritableByteChannel targetChannel = output.getChannel();
for (int i = 0; i < inF.length; i++) {
FileInputStream input = new FileInputStream(inF[i]);
FileChannel inputChannel = input.getChannel();
inputChannel.transferTo(0, inputChannel.size(), targetChannel);
inputChannel.close();
input.close();
}
targetChannel.close();
output.close();
}
}
Java NIO中核心緩衝區如下:CharBuffer,DoubleBuffer,IntBuffer,LongBuffer,ByteBuffer,ShortBuffer 和 FloatBuffer。這些緩衝區覆蓋了通過I/O傳送的基本資料型別:characters,double,int,long,byte,short 和 float。
基本緩衝區BufferedReader示例:
public class BufferedDemo {
public static void main(String[] args) {
try (InputStream inputStream = Files.newInputStream(Paths.get("article.json"));
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))){
System.out.println(bufferedReader.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
}
在Java NIO中,選擇器(Selector)是可選擇通道的多路複用器,可用作可以進入非阻塞模式的特殊型別的通道。它可以檢查一個或多個NIO通道,並確定哪個通道準備好了可以進行通訊,即讀取或寫入。
選擇器(Selector)用於使用單個執行緒處理多個通道。 因此,它需要較少的執行緒來處理這些通道。 執行緒之間的切換對於作業系統來說是昂貴的。 因此,使用它可以提高系統效率。