java.io和util的區別_Java NIO與IO的區別和比較

Java NIO與IO的區別和比較

導讀

J2SE1.4以上版本中發布了全新的I/O類庫。本文將通過一些實例來簡單介紹NIO庫提供的一些新特性:非阻塞I/O,字符轉換,緩沖以及通道。

一. 介紹NIO

NIO包(java.nio.*)引入了四個關鍵的抽象數據類型,它們共同解決傳統的I/O類中的一些問題。Buffer:它是包含數據且用于讀寫的線形表結構。其中還提供了一個特殊類用于內存映射文件的I/O操作。

Charset:它提供Unicode字符串影射到字節序列以及逆影射的操作。

Channels:包含socket,file和pipe三種管道,它實際上是雙向交流的通道。

Selector:它將多元異步I/O操作集中到一個或多個線程中(它可以被看成是Unix中select()函數或Win32中WaitForSingleEvent()函數的面向對象版本)。

二. 回顧傳統

在介紹NIO之前,有必要了解傳統的I/O操作的方式。以網絡應用為例,傳統方式需要監聽一個ServerSocket,接受請求的連接為其提供服務(服務通常包括了處理請求并發送響應)圖一是服務器的生命周期圖,其中標有粗黑線條的部分表明會發生I/O阻塞。

92118d80e076a39b973f7b62490c1baa.png

圖一

//可以分析創建服務器的每個具體步驟。首先創建ServerSocket

ServerSocket server=new ServerSocket(10000);//然后接受新的連接請求

Socket newConnection=server.accept();//對于accept方法的調用將造成阻塞,直到ServerSocket接受到一個連接請求為止。一旦連接請求被接受,服務器可以讀客戶socket中的請求。

InputStream in =newConnection.getInputStream();

InputStreamReader reader= newInputStreamReader(in);

BufferedReader buffer= newBufferedReader(reader);

Request request= newRequest();while(!request.isComplete()) {

String line=buffer.readLine();

request.addLine(line);

這樣的操作有兩個問題,首先BufferedReader類的readLine()方法在其緩沖區未滿時會造成線程阻塞,只有一定數據填滿了緩沖區或者客戶關閉了套接字,方法才會返回。其次,它回產生大量的垃圾,BufferedReader創建了緩沖區來從客戶套接字讀入數據,但是同樣創建了一些字符串存儲這些數據。雖然BufferedReader內部提供了StringBuffer處理這一問題,但是所有的String很快變成了垃圾需要回收。

同樣的問題在發送響應代碼中也存在

Response response =request.generateResponse();

OutputStream out=newConnection.getOutputStream();

InputStream in=response.getInputStream();intch;while(-1 != (ch =in.read())) {

out.write(ch);

}

newConnection.close();

類似的,讀寫操作被阻塞而且向流中一次寫入一個字符會造成效率低下,所以應該使用緩沖區,但是一旦使用緩沖,流又會產生更多的垃圾。

傳統的解決方法

通常在Java中處理阻塞I/O要用到線程(大量的線程)。一般是實現一個線程池用來處理請求,如圖二

34ba5b548f7fc838581d39b7f38059d7.png

圖二

線程使得服務器可以處理多個連接,但是它們也同樣引發了許多問題。每個線程擁有自己的棧空間并且占用一些CPU時間,耗費很大,而且很多時間是浪費在阻塞的I/O操作上,沒有有效的利用CPU。

三. 新I/O

1.?Buffer

傳統的I/O不斷的浪費對象資源(通常是String)。新I/O通過使用Buffer讀寫數據避免了資源浪費。Buffer對象是線性的,有序的數據集合,它根據其類別只包含唯一的數據類型。

java.nio.Buffer //類描述

java.nio.ByteBuffer //包含字節類型。 可以從ReadableByteChannel中讀在 WritableByteChannel中寫

java.nio.MappedByteBuffer //包含字節類型,直接在內存某一區域映射

java.nio.CharBuffer //包含字符類型,不能寫入通道

java.nio.DoubleBuffer //包含double類型,不能寫入通道

java.nio.FloatBuffer //包含float類型

java.nio.IntBuffer //包含int類型

java.nio.LongBuffer //包含long類型

java.nio.ShortBuffer //包含short類型

可以通過調用allocate(int capacity)方法或者allocateDirect(int capacity)方法分配一個Buffer。特別的,你可以創建MappedBytesBuffer通過調用FileChannel.map(int mode,long position,int size)。直接(direct)buffer在內存中分配一段連續的塊并使用本地訪問方法讀寫數據。非直接(nondirect)buffer通過使用Java中的數組訪問代碼讀寫數據。有時候必須使用非直接緩沖例如使用任何的wrap方法(如ByteBuffer.wrap(byte[]))在Java數組基礎上創建buffer。

2. 字符編碼

向ByteBuffer中存放數據涉及到兩個問題:字節的順序和字符轉換。ByteBuffer內部通過ByteOrder類處理了字節順序問題,但是并沒有處理字符轉換。事實上,ByteBuffer沒有提供方法讀寫String。

Java.nio.charset.Charset處理了字符轉換問題。它通過構造CharsetEncoder和CharsetDecoder將字符序列轉換成字節和逆轉換。

3. 通道(Channel)

你可能注意到現有的java.io類中沒有一個能夠讀寫Buffer類型,所以NIO中提供了Channel類來讀寫Buffer。通道可以認為是一種連接,可以是到特定設備,程序或者是網絡的連接。通道的類等級結構圖如下

06f4f2c94bf9797d9c3b86d4e118e086.png

圖三

圖中ReadableByteChannel和WritableByteChannel分別用于讀寫。

GatheringByteChannel可以從使用一次將多個Buffer中的數據寫入通道,相反的,ScatteringByteChannel則可以一次將數據從通道讀入多個Buffer中。你還可以設置通道使其為阻塞或非阻塞I/O操作服務。

為了使通道能夠同傳統I/O類相容,Channel類提供了靜態方法創建Stream或Reader

4.?Selector

在過去的阻塞I/O中,我們一般知道什么時候可以向stream中讀或寫,因為方法調用直到stream準備好時返回。但是使用非阻塞通道,我們需要一些方法來知道什么時候通道準備好了。在NIO包中,設計Selector就是為了這個目的。SelectableChannel可以注冊特定的事件,而不是在事件發生時通知應用,通道跟蹤事件。然后,當應用調用Selector上的任意一個selection方法時,它查看注冊了的通道看是否有任何感興趣的事件發生。圖四是selector和兩個已注冊的通道的例子

73baa534e24b22bc89bfdbd5dd152696.png

并不是所有的通道都支持所有的操作。SelectionKey類定義了所有可能的操作位,將要用兩次。首先,當應用調用SelectableChannel.register(Selector sel,int op)方法注冊通道時,它將所需操作作為第二個參數傳遞到方法中。然后,一旦SelectionKey被選中了,SelectionKey的readyOps()方法返回所有通道支持操作的數位的和。SelectableChannel的validOps方法返回每個通道允許的操作。注冊通道不支持的操作將引發IllegalArgumentException異常。下表列出了SelectableChannel子類所支持的操作。

IllegalArgumentException異常。下表列出了SelectableChannel子類所支持的操作。

ServerSocketChannel OP_ACCEPT

SocketChannel OP_CONNECT, OP_READ, OP_WRITE

DatagramChannel OP_READ, OP_WRITE

Pipe.SourceChannel OP_READ

Pipe.SinkChannel OP_WRITE

四. 舉例說明

1. 簡單網頁內容下載

這個例子非常簡單,類SocketChannelReader使用SocketChannel來下載特定網頁的HTML內容。

importjava.nio.ByteBuffer;importjava.nio.channels.SocketChannel;importjava.nio.charset.Charset;importjava.net.InetSocketAddress;importjava.io.IOException;public classSocketChannelReader {private Charset charset = Charset.forName("UTF-8");//創建UTF-8字符集

privateSocketChannel channel;public voidgetHTMLContent() {try{

connect();

sendRequest();

readResponse();

}catch(IOException e) {

System.err.println(e.toString());

}finally{if (channel != null) {try{

channel.close();

}catch(IOException e) {

}

}

}

}private void connect() throws IOException {//連接到CSDN

InetSocketAddress socketAddress = newInetSocketAddress("www.csdn.net", 80);

channel=SocketChannel.open(socketAddress);//使用工廠方法open創建一個channel并將它連接到指定地址上//相當與SocketChannel.open().connect(socketAddress);調用

}private void sendRequest() throwsIOException {

channel.write(charset.encode("GET " + "/document" + "\r\n\r\n"));//發送GET請求到CSDN的文檔中心//使用channel.write方法,它需要CharByte類型的參數,使用//Charset.encode(String)方法轉換字符串。

}private void readResponse() throws IOException {//讀取應答

ByteBuffer buffer = ByteBuffer.allocate(1024);//創建1024字節的緩沖

while (channel.read(buffer) != -1) {

buffer.flip();//flip方法在讀緩沖區字節操作之前調用。

System.out.println(charset.decode(buffer));//使用Charset.decode方法將字節轉換為字符串

buffer.clear();//清空緩沖

}

}public static voidmain(String[] args) {try{newSocketChannelReader().getHTMLContent();

}catch(Exception e) {

e.printStackTrace();

}

}

}

2. 簡單的加法服務器和客戶機

服務器代碼

packagemjorcen.nio.test;importjava.nio.ByteBuffer;importjava.nio.IntBuffer;importjava.nio.channels.ServerSocketChannel;importjava.nio.channels.SocketChannel;importjava.net.InetSocketAddress;importjava.io.IOException;/*** SumServer.java

*

*

* Created: Thu Nov 06 11:41:52 2003

*

*@authorstarchu1981

*@version1.0*/

public classSumServer {private ByteBuffer _buffer = ByteBuffer.allocate(8);private IntBuffer _intBuffer =_buffer.asIntBuffer();private SocketChannel _clientChannel = null;private ServerSocketChannel _serverChannel = null;public voidstart() {try{

openChannel();

waitForConnection();

}catch(IOException e) {

System.err.println(e.toString());

}

}private void openChannel() throwsIOException {

_serverChannel=ServerSocketChannel.open();

_serverChannel.socket().bind(new InetSocketAddress(10000));

System.out.println("服務器通道已經打開");

}private void waitForConnection() throwsIOException {while (true) {

_clientChannel=_serverChannel.accept();if (_clientChannel != null) {

System.out.println("新的連接加入");

processRequest();

_clientChannel.close();

}

}

}private void processRequest() throwsIOException {

_buffer.clear();

_clientChannel.read(_buffer);int result = _intBuffer.get(0) + _intBuffer.get(1);

_buffer.flip();

_buffer.clear();

_intBuffer.put(0, result);

_clientChannel.write(_buffer);

}public static voidmain(String[] args) {newSumServer().start();

}

}//SumServer

客戶代碼

packagemjorcen.nio.test;importjava.nio.ByteBuffer;importjava.nio.IntBuffer;importjava.nio.channels.SocketChannel;importjava.net.InetSocketAddress;importjava.io.IOException;/*** SumClient.java

*

*

* Created: Thu Nov 06 11:26:06 2003

*

*@authorstarchu1981

*@version1.0*/

public classSumClient {private ByteBuffer _buffer = ByteBuffer.allocate(8);privateIntBuffer _intBuffer;privateSocketChannel _channel;publicSumClient() {

_intBuffer=_buffer.asIntBuffer();

}//SumClient constructor

public int getSum(int first, intsecond) {int result = 0;try{

_channel=connect();

sendSumRequest(first, second);

result=receiveResponse();

}catch(IOException e) {

System.err.println(e.toString());

}finally{if (_channel != null) {try{

_channel.close();

}catch(IOException e) {

}

}

}returnresult;

}private SocketChannel connect() throwsIOException {

InetSocketAddress socketAddress= new InetSocketAddress("localhost",10000);returnSocketChannel.open(socketAddress);

}private void sendSumRequest(int first, int second) throwsIOException {

_buffer.clear();

_intBuffer.put(0, first);

_intBuffer.put(1, second);

_channel.write(_buffer);

System.out.println("發送加法請求 " + first + "+" +second);

}private int receiveResponse() throwsIOException {

_buffer.clear();

_channel.read(_buffer);return _intBuffer.get(0);

}public static voidmain(String[] args) {

SumClient sumClient= newSumClient();

System.out.println("加法結果為 :" + sumClient.getSum(100, 324));

}

}//SumClient

3. 非阻塞的加法服務器

首先在openChannel方法中加入語句

_serverChannel.configureBlocking(false);//設置成為非阻塞模式

重寫WaitForConnection方法的代碼如下,使用非阻塞方式 (這里我稍微改了下,不知道對不對, 原來博主的跑不了,)

private void waitForConnection() throwsIOException {

Selector acceptSelector=SelectorProvider.provider().openSelector();/** 在服務器套接字上注冊selector并設置為接受accept方法的通知。

* 這就告訴Selector,套接字想要在accept操作發生時被放在ready表 上,因此,允許多元非阻塞I/O發生。*/SelectionKey acceptKey=_serverChannel.register(acceptSelector,

SelectionKey.OP_ACCEPT);int keysAdded = 0;/*select方法在任何上面注冊了的操作發生時返回*/

while ((keysAdded = acceptSelector.select()) > 0) {//某客戶已經準備好可以進行I/O操作了,獲取其ready鍵集合

Set readyKeys =acceptSelector.selectedKeys();

Iterator i=readyKeys.iterator();//遍歷ready鍵集合,并處理加法請求

while(i.hasNext()) {

SelectionKey sk=(SelectionKey) i.next();

i.remove();

ServerSocketChannel nextReady=(ServerSocketChannel) sk

.channel();//接受加法請求并處理它

_clientChannel =nextReady.accept().socket().getChannel();

processRequest();

_clientChannel.close();

}

}

}

服務端整體改完之后代碼:

packagemjorcen.nio.test;importjava.io.IOException;importjava.net.InetSocketAddress;importjava.net.Socket;importjava.nio.ByteBuffer;importjava.nio.IntBuffer;importjava.nio.channels.SelectableChannel;importjava.nio.channels.SelectionKey;importjava.nio.channels.Selector;importjava.nio.channels.ServerSocketChannel;importjava.nio.channels.SocketChannel;importjava.nio.channels.spi.SelectorProvider;importjava.util.Iterator;importjava.util.Set;/*** SumServer.java

*

*

* Created: Thu Nov 06 11:41:52 2003

*

*@authorstarchu1981

*@version1.0*/

public classSumServer {private ByteBuffer _buffer = ByteBuffer.allocate(8);private IntBuffer _intBuffer =_buffer.asIntBuffer();private SocketChannel _clientChannel = null;private ServerSocketChannel _serverChannel = null;public voidstart() {try{

openChannel();

waitForConnection();

}catch(IOException e) {

System.err.println(e.toString());

}

}private void openChannel() throwsIOException {

_serverChannel=ServerSocketChannel.open();

_serverChannel.socket().bind(new InetSocketAddress(10000));

_serverChannel.configureBlocking(false);//設置成為非阻塞模式

System.out.println("服務器通道已經打開");

}private void waitForConnection() throwsIOException {

Selector acceptSelector=SelectorProvider.provider().openSelector();/** 在服務器套接字上注冊selector并設置為接受accept方法的通知。

* 這就告訴Selector,套接字想要在accept操作發生時被放在ready表 上,因此,允許多元非阻塞I/O發生。*/SelectionKey acceptKey=_serverChannel.register(acceptSelector,

SelectionKey.OP_ACCEPT);int keysAdded = 0;/*select方法在任何上面注冊了的操作發生時返回*/

while ((keysAdded = acceptSelector.select()) > 0) {//某客戶已經準備好可以進行I/O操作了,獲取其ready鍵集合

Set readyKeys =acceptSelector.selectedKeys();

Iterator i=readyKeys.iterator();//遍歷ready鍵集合,并處理加法請求

while(i.hasNext()) {

SelectionKey sk=(SelectionKey) i.next();

i.remove();

ServerSocketChannel nextReady=(ServerSocketChannel) sk

.channel();//接受加法請求并處理它

_clientChannel =nextReady.accept().socket().getChannel();

processRequest();

_clientChannel.close();

}

}

}private void processRequest() throwsIOException {

_buffer.clear();

_clientChannel.read(_buffer);int result = _intBuffer.get(0) + _intBuffer.get(1);

_buffer.flip();

_buffer.clear();

_intBuffer.put(0, result);

_clientChannel.write(_buffer);

}public static voidmain(String[] args) {newSumServer().start();

}

}//SumServer

來自 :  http://blog.chinaunix.net/uid-24186189-id-2623973.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/541925.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/541925.shtml
英文地址,請注明出處:http://en.pswp.cn/news/541925.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java LocalDate類| isSupported()方法與示例

LocalDate類isSupported()方法 (LocalDate Class isSupported() method) Syntax: 句法: public boolean isSupported (TemporalField t_field);public boolean isSupported (TemporalUnit t_unit);isSupported() method is available in java.time package. isSupp…

區塊鏈+稅務的思考

2016年,區塊鏈技術火了!各大金融公司、互聯網巨頭都競相參加到區塊鏈技術的研究中。我們公司的業務是稅務的信息化領域,也希望通過區塊鏈技術的應用,來提升為財稅領域的服務。 區塊鏈技術優缺點總結 下圖是對區塊鏈技術的一些特點…

java hasset 順序_java集合排序問題

List: 元素是有序的,元素可以重復,因為該集合體系有索引(腳標)常用的子類對象:1————ArrayList 底層的數據結構是使用的數組結構特點:查詢速度快,但是增刪比較慢2————LinkedList底層的數據結構使用的是鏈表結構…

如何使用JavaScript刪除CSS屬性?

In this article, well see how we can remove a CSS property from a certain element using JavaScript? We can remove only those properties that we assign ourselves and the pre-default ones cannot be removed by this method. 在本文中,我們將看到如何使…

Django 緩存系統

Django 是動態網站,一般來說需要實時地生成訪問的網頁,展示給訪問者,這樣,內容可以隨時變化,但是從數據庫讀多次把所需要的數據取出來,要比從內存或者硬盤等一次讀出來 付出的成本大很多。 緩存系統工作原理…

java web截屏_java_WebDriver中實現對特定的Web區域截圖方法,用過 WebDriver 的同學都知道,We - phpStudy...

WebDriver中實現對特定的Web區域截圖方法用過 WebDriver 的同學都知道,WebDriver 可以對瀏覽器中的頁面進行截圖。例如:public byte[] takeScreenshot() throws IOException {TakesScreenshot takesScreenshot (TakesScreenshot) driver;return takesSc…

c語言 關鍵字const_C ++ const關鍵字| 查找輸出程序| 套裝1

c語言 關鍵字constProgram 1: 程序1&#xff1a; #include <iostream>using namespace std;void fun(int& A) const{A 10;}int main(){int X 0;fun(X);cout << X;return 0;}Output: 輸出&#xff1a; [Error] non-member function void fun(int) cannot ha…

【喜報】JEEWX榮獲“2016 年度碼云新增熱門開源軟件排行榜”第一名!

為什么80%的碼農都做不了架構師&#xff1f;>>> 2016 年度碼云新增項目排行榜 TOP 50 正式出爐&#xff01;根據 2016 年在碼云上新增開源項目的 Watch、Star、Fork 數量以及其他角度的統計&#xff0c;JEEWX捷微管家榮獲“2016 年度碼云新增熱門開源軟件排行榜”第…

java 二叉樹特點_瘋狂java筆記之樹和二叉樹

樹的概述樹是一種非常常用的數據結構&#xff0c;樹與前面介紹的線性表&#xff0c;棧&#xff0c;隊列等線性結構不同&#xff0c;樹是一種非線性結構1.樹的定義和基本術語計算機世界里的樹&#xff0c;是從自然界中實際的樹抽象而來的&#xff0c;它指的是N個有父子關系的節點…

編輯距離 dp_使用動態編程(DP)編輯距離

編輯距離 dpProblem: You are given two strings s1 and s2 of length M and N respectively. You can perform following operations on the string. 問題&#xff1a;給您兩個長度分別為M和N的字符串s1和s2 。 您可以對字符串執行以下操作。 Insert a character at any posi…

tomcat +apache 配置集群

2019獨角獸企業重金招聘Python工程師標準>>> APACHE2.2.25TOMCAT6.0.37配置負載均衡 目標: 使用 apache 和 tomcat 配置一個可以應用的 web 網站&#xff0c;要達到以下要求&#xff1a; 1. Apache 做為 HttpServer &#xff0c;后面連接多個 tomcat 應用實例&…

java雙緩存機制_詳解JVM類加載機制及類緩存問題的處理方法

前言大家應該都知道&#xff0c;當一個Java項目啟動的時候&#xff0c;JVM會找到main方法&#xff0c;根據對象之間的調用來對class文件和所引用的jar包中的class文件進行加載(其步驟分為加載、驗證、準備、解析、初始化、使用和卸載)&#xff0c;方法區中開辟內存來存儲類的運…

oracle中dbms_并發和由于DBMS中的并發導致的問題

oracle中dbms并發 (Concurrency) The ability of a database system which handles simultaneously or a number of transactions by interleaving parts of the actions or the overlapping this is called concurrency of the system. 數據庫系統通過交織部分操作或重疊操作來…

什么是mvc?

什么是MVCMVC 是一種設計模式&#xff0c;它將應用劃分為3 個部分&#xff1a;數據&#xff08;模型&#xff09;、展現層&#xff08;視圖&#xff09;和用戶交互層&#xff08;控制器&#xff09;。換句話說&#xff0c;一個事件的發生是這樣的過程&#xff1a;1&#xff0e;…

mysql的安裝和基本命令_MySQL安裝以及簡單命令用法

MYSQL:關系型數據庫存儲引擎:負責將邏輯層的概念轉化為物理層機制&#xff0c;在物理層完成物理機制。支持事務&#xff1a;transaction必須滿足的條件&#xff1a;ACID(一致性,持久性,原子性,隔離性)鎖&#xff1a;并發訪問隨機訪問&#xff1a;數據在磁盤上是隨機存儲的安裝&…

將數組轉換為JavaScript中的對象

Lets say you have the following array, 假設您有以下數組&#xff0c; const nums [1, 2, 3, 4, 5];console.log(nums);Output 輸出量 (5) [1, 2, 3, 4, 5]We know that nums is an array and we can see in the output that we get an array. Lets convert it into an ob…

docker集群運行在calico網絡上

2019獨角獸企業重金招聘Python工程師標準>>> ##網絡及版本信息 docker1 centos7 192.168.75.200 docker2 centos7 192.168.75.201 物理網絡 192.168.75.1/24 Docker version 1.10.3, build 3999ccb-unsupported &#xff0c;安裝過程略 # calicoctl version Version…

python批量雷達圖_python批量制作雷達圖

老板要畫雷達圖&#xff0c;但是數據好多組怎么辦&#xff1f;不能一個一個點excel去畫吧&#xff0c;那么可以利用python進行批量制作&#xff0c;得到樣式如下&#xff1a;首先制作一個演示的excel&#xff0c;評分為excel隨機數生成&#xff1a;1 INT((RAND()4)*10)/10加入標…

JavaScript中帶有示例的Math.log()方法

JavaScript | Math.log()方法 (JavaScript | Math.log() Method) Math.log() is a function in math library of JavaScript that is used to return the value of natural Log i.e. (base e) of the given number. It is also known as ln(x) in mathematical terms. Math.log…

SUI踩坑記錄

SUI踩坑記錄 最近做了個項目選型了SUI和vue做單頁應用。下面記錄一下踩坑經歷SUI 介紹 sui文檔&#xff1a;http://m.sui.taobao.org/SUI Mobile 是一套基于 Framework7 開發的UI庫。它非常輕量、精美&#xff0c;只需要引入我們的CDN文件就可以使用&#xff0c;并且能兼容到 i…