日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java修炼之道--I/O

發(fā)布時(shí)間:2025/3/15 java 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java修炼之道--I/O 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

原作地址:https://github.com/frank-lam/2019_campus_apply

Java IO

Java 的 I/O 大概可以分成以下幾類:

  • 磁盤操作:File
  • 字節(jié)操作:InputStream 和 OutputStream
  • 字符操作:Reader 和 Writer
  • 對(duì)象操作:Serializable
  • 網(wǎng)絡(luò)操作:Socket
  • 新的輸入/輸出:NIO

1、磁盤操作(File)

File 類可以用于表示文件和目錄的信息,但是它不表示文件的內(nèi)容。

遞歸地輸出一個(gè)目錄下所有文件:

public static void listAllFiles(File dir) {if (dir == null || !dir.exists()) {return;}if (dir.isFile()) {System.out.println(dir.getName());return;}for (File file : dir.listFiles()) {listAllFiles(file);} }

2、字節(jié)操作(*Stream)

使用字節(jié)流操作進(jìn)行文件復(fù)制:

public static void copyFile(String src, String dist) throws IOException {FileInputStream in = new FileInputStream(src);FileOutputStream out = new FileOutputStream(dist);byte[] buffer = new byte[20 * 1024];// read() 最多讀取 buffer.length 個(gè)字節(jié)// 返回的是實(shí)際讀取的個(gè)數(shù)// 返回 -1 的時(shí)候表示讀到 eof,即文件尾while (in.read(buffer, 0, buffer.length) != -1) {out.write(buffer);}in.close();out.close(); }


Java I/O 使用了裝飾者模式來實(shí)現(xiàn)。以 InputStream 為例,InputStream 是抽象組件,FileInputStream 是 InputStream 的子類,屬于具體組件,提供了字節(jié)流的輸入操作。FilterInputStream 屬于抽象裝飾者,裝飾者用于裝飾組件,為組件提供額外的功能,例如 BufferedInputStream 為 FileInputStream 提供緩存的功能。

實(shí)例化一個(gè)具有緩存功能的字節(jié)流對(duì)象時(shí),只需要在 FileInputStream 對(duì)象上再套一層 BufferedInputStream 對(duì)象即可。

FileInputStream fileInputStream = new FileInputStream(filePath); BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);

DataInputStream 裝飾者提供了對(duì)更多數(shù)據(jù)類型進(jìn)行輸入的操作,比如 int、double 等基本類型。

3、字符操作(Reader | Writer)

不管是磁盤還是網(wǎng)絡(luò)傳輸,最小的存儲(chǔ)單元都是字節(jié),而不是字符。但是在程序中操作的通常是字符形式的數(shù)據(jù),因此需要提供對(duì)字符進(jìn)行操作的方法。

  • InputStreamReader 實(shí)現(xiàn)從字節(jié)流解碼成字符流
  • OutputStreamWriter 實(shí)現(xiàn)字符流編碼成為字節(jié)流

逐行輸出文本文件的內(nèi)容:

public static void readFileContent(String filePath) throws IOException {FileReader fileReader = new FileReader(filePath);BufferedReader bufferedReader = new BufferedReader(fileReader);String line;while ((line = bufferedReader.readLine()) != null) {System.out.println(line);}// 裝飾者模式使得 BufferedReader 組合了一個(gè) Reader 對(duì)象// 在調(diào)用 BufferedReader 的 close() 方法時(shí)會(huì)去調(diào)用 fileReader 的 close() 方法// 因此只要一個(gè) close() 調(diào)用即可bufferedReader.close(); }

編碼就是把字符轉(zhuǎn)換為字節(jié),而解碼是把字節(jié)重新組合成字符。

如果編碼和解碼過程使用不同的編碼方式那么就出現(xiàn)了亂碼。

  • GBK 編碼中,中文字符占 2 個(gè)字節(jié),英文字符占 1 個(gè)字節(jié);
  • UTF-8 編碼中,中文字符占 3 個(gè)字節(jié),英文字符占 1 個(gè)字節(jié);
  • UTF-16be 編碼中,中文字符和英文字符都占 2 個(gè)字節(jié)。

UTF-16be 中的 be 指的是 Big Endian,也就是大端。相應(yīng)地也有 UTF-16le,le 指的是 Little Endian,也就是小端。

Java 使用雙字節(jié)編碼 UTF-16be,這不是指 Java 只支持這一種編碼方式,而是說 char 這種類型使用 UTF-16be 進(jìn)行編碼。char 類型占 16 位,也就是兩個(gè)字節(jié),Java 使用這種雙字節(jié)編碼是為了讓一個(gè)中文或者一個(gè)英文都能使用一個(gè) char 來存儲(chǔ)。

String 可以看成一個(gè)字符序列,可以指定一個(gè)編碼方式將它轉(zhuǎn)換為字節(jié)序列,也可以指定一個(gè)編碼方式將一個(gè)字節(jié)序列轉(zhuǎn)換為 String。

String str1 = "中文"; byte[] bytes = str1.getBytes("UTF-8"); String str2 = new String(bytes, "UTF-8"); System.out.println(str2);

在調(diào)用無參數(shù) getBytes() 方法時(shí),默認(rèn)的編碼方式不是 UTF-16be。雙字節(jié)編碼的好處是可以使用一個(gè) char 存儲(chǔ)中文和英文,而將 String 轉(zhuǎn)為 bytes[] 字節(jié)數(shù)組就不再需要這個(gè)好處,因此也就不再需要雙字節(jié)編碼。getBytes() 的默認(rèn)編碼方式與平臺(tái)有關(guān),一般為 UTF-8。

byte[] bytes = str1.getBytes();

4、Java序列化,如何實(shí)現(xiàn)序列化和反序列化,常見的序列化協(xié)議有哪些?

Java序列化定義

(1)Java序列化是指把Java對(duì)象轉(zhuǎn)換為字節(jié)序列的過程,而Java反序列化是指把字節(jié)序列恢復(fù)為Java對(duì)象的過程;

(2)序列化:對(duì)象序列化的最主要的用處就是在傳遞和保存對(duì)象的時(shí)候,保證對(duì)象的完整性和可傳遞性。序列化是把對(duì)象轉(zhuǎn)換成有序字節(jié)流,以便在網(wǎng)絡(luò)上傳輸或者保存在本地文件中。序列化后的字節(jié)流保存了Java對(duì)象的狀態(tài)以及相關(guān)的描述信息。序列化機(jī)制的核心作用就是對(duì)象狀態(tài)的保存與重建

(3)反序列化:客戶端從文件中或網(wǎng)絡(luò)上獲得序列化后的對(duì)象字節(jié)流后,根據(jù)字節(jié)流中所保存的對(duì)象狀態(tài)及描述信息,通過反序列化重建對(duì)象。

(4)本質(zhì)上講,序列化就是把實(shí)體對(duì)象狀態(tài)按照一定的格式寫入到有序字節(jié)流,反序列化就是從有序字節(jié)流重建對(duì)象,恢復(fù)對(duì)象狀態(tài)。

如何實(shí)現(xiàn)序列化和反序列化,底層怎么實(shí)現(xiàn)

1、JDK類庫(kù)中序列化和反序列化API

(1)java.io.ObjectOutputStream:表示對(duì)象輸出流;

它的writeObject(Object obj)方法可以對(duì)參數(shù)指定的obj對(duì)象進(jìn)行序列化,把得到的字節(jié)序列寫到一個(gè)目標(biāo)輸出流中;

(2)java.io.ObjectInputStream:表示對(duì)象輸入流;

它的readObject()方法源輸入流中讀取字節(jié)序列,再把它們反序列化成為一個(gè)對(duì)象,并將其返回;

2、實(shí)現(xiàn)序列化的要求

只有實(shí)現(xiàn)了 Serializable 或 Externalizable 接口的類的對(duì)象才能被序列化,否則拋出異常!

3、實(shí)現(xiàn)Java對(duì)象序列化與反序列化的方法

  假定一個(gè)User類,它的對(duì)象需要序列化,可以有如下三種方法:

  • 若 User 類僅僅實(shí)現(xiàn)了 Serializable 接口,則可以按照以下方式進(jìn)行序列化和反序列化
    • ObjectOutputStream 采用默認(rèn)的序列化方式,對(duì) User 對(duì)象的非 transient 的實(shí)例變量進(jìn)行序列化。
    • ObjcetInputStream 采用默認(rèn)的反序列化方式,對(duì)對(duì) User 對(duì)象的非 transient 的實(shí)例變量進(jìn)行反序列化。
  • 若User類僅僅實(shí)現(xiàn)了Serializable接口,并且還定義了 readObject(ObjectInputStream in) 和writeObject(ObjectOutputSteam out),則采用以下方式進(jìn)行序列化與反序列化。
    • ObjectOutputStream 調(diào)用 User 對(duì)象的 writeObject(ObjectOutputStream out) 的方法進(jìn)行序列化。
    • ObjectInputStream 會(huì)調(diào)用 User 對(duì)象的 readObject(ObjectInputStream in) 的方法進(jìn)行反序列化。
  • 若User類實(shí)現(xiàn)了 Externalnalizable 接口,且 User 類必須實(shí)現(xiàn) readExternal(ObjectInput in) 和 writeExternal(ObjectOutput out) 方法,則按照以下方式進(jìn)行序列化與反序列化。
    • ObjectOutputStream 調(diào)用 User 對(duì)象的 writeExternal(ObjectOutput out)) 的方法進(jìn)行序列化。
    • ObjectInputStream 會(huì)調(diào)用User對(duì)象的 readExternal(ObjectInput in) 的方法進(jìn)行反序列化。

4、JDK類庫(kù)中序列化的步驟

步驟一:創(chuàng)建一個(gè)對(duì)象輸出流,它可以包裝一個(gè)其它類型的目標(biāo)輸出流,如文件輸出流:

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\object.out"));

步驟二:通過對(duì)象輸出流的writeObject()方法寫對(duì)象:

oos.writeObject(new User("xuliugen", "123456", "male"));

5、JDK類庫(kù)中反序列化的步驟

步驟一:創(chuàng)建一個(gè)對(duì)象輸入流,它可以包裝一個(gè)其它類型輸入流,如文件輸入流:

ObjectInputStream ois= new ObjectInputStream(new FileInputStream("object.out"));

步驟二:通過對(duì)象輸出流的readObject()方法讀取對(duì)象:

User user = (User) ois.readObject();

說明:為了正確讀取數(shù)據(jù),完成反序列化,必須保證向?qū)ο筝敵隽鲗憣?duì)象的順序與從對(duì)象輸入流中讀對(duì)象的順序一致。

6、序列化和反序列化的示例

為了更好地理解Java序列化與反序列化,舉一個(gè)簡(jiǎn)單的示例如下:

public class SerialDemo {public static void main(String[] args) throws IOException, ClassNotFoundException {//序列化FileOutputStream fos = new FileOutputStream("object.out");ObjectOutputStream oos = new ObjectOutputStream(fos);User user1 = new User("xuliugen", "123456", "male");oos.writeObject(user1);oos.flush();oos.close();//反序列化FileInputStream fis = new FileInputStream("object.out");ObjectInputStream ois = new ObjectInputStream(fis);User user2 = (User) ois.readObject();System.out.println(user2.getUserName()+ " " + user2.getPassword() + " " + user2.getSex());//反序列化的輸出結(jié)果為:xuliugen 123456 male} }public class User implements Serializable {private String userName;private String password;private String sex;//全參構(gòu)造方法、get和set方法省略 }

相關(guān)注意事項(xiàng)

1、序列化時(shí),只對(duì)對(duì)象的狀態(tài)進(jìn)行保存,而不管對(duì)象的方法;

2、當(dāng)一個(gè)父類實(shí)現(xiàn)序列化,子類自動(dòng)實(shí)現(xiàn)序列化,不需要顯式實(shí)現(xiàn) Serializable 接口;

3、當(dāng)一個(gè)對(duì)象的實(shí)例變量引用其他對(duì)象,序列化該對(duì)象時(shí)也把引用對(duì)象進(jìn)行序列化;

4、并非所有的對(duì)象都可以序列化,至于為什么不可以,有很多原因了,比如:

  • 安全方面的原因,比如一個(gè)對(duì)象擁有private,public等field,對(duì)于一個(gè)要傳輸?shù)膶?duì)象,比如寫到文件,或者進(jìn)行RMI傳輸?shù)鹊?#xff0c;在序列化進(jìn)行傳輸?shù)倪^程中,這個(gè)對(duì)象的private等域是不受保護(hù)的;
  • 資源分配方面的原因,比如socket,thread類,如果可以序列化,進(jìn)行傳輸或者保存,也無法對(duì)他們進(jìn)行重新的資源分配,而且,也是沒有必要這樣實(shí)現(xiàn);

5、聲明為static和transient類型的成員數(shù)據(jù)不能被序列化。因?yàn)閟tatic代表類的狀態(tài),transient代表對(duì)象的臨時(shí)數(shù)據(jù)。

6、序列化運(yùn)行時(shí)使用一個(gè)稱為 serialVersionUID 的版本號(hào)與每個(gè)可序列化類相關(guān)聯(lián),該序列號(hào)在反序列化過程中用于驗(yàn)證序列化對(duì)象的發(fā)送者和接收者是否為該對(duì)象加載了與序列化兼容的類。為它賦予明確的值。顯式地定義serialVersionUID有兩種用途:

  • 在某些場(chǎng)合,希望類的不同版本對(duì)序列化兼容,因此需要確保類的不同版本具有相同的serialVersionUID;
  • 在某些場(chǎng)合,不希望類的不同版本對(duì)序列化兼容,因此需要確保類的不同版本具有不同的serialVersionUID。

7、Java有很多基礎(chǔ)類已經(jīng)實(shí)現(xiàn)了serializable接口,比如String , Vector等。但是也有一些沒有實(shí)現(xiàn)serializable接口的;

8、如果一個(gè)對(duì)象的成員變量是一個(gè)對(duì)象,那么這個(gè)對(duì)象的數(shù)據(jù)成員也會(huì)被保存!這是能用序列化解決深拷貝的重要原因;

ArrayList 序列化和反序列化的實(shí)現(xiàn) :ArrayList 中存儲(chǔ)數(shù)據(jù)的數(shù)組是用 transient 修飾的,因?yàn)檫@個(gè)數(shù)組是動(dòng)態(tài)擴(kuò)展的,并不是所有的空間都被使用,因此就不需要所有的內(nèi)容都被序列化。通過重寫序列化和反序列化方法,使得可以只序列化數(shù)組中有內(nèi)容的那部分?jǐn)?shù)據(jù)。

private transient Object[] elementData;

參考資料:

  • 序列化和反序列化的底層實(shí)現(xiàn)原理是什么? - CSDN博客

常見的序列化協(xié)議有哪些

  • COM主要用于Windows平臺(tái),并沒有真正實(shí)現(xiàn)跨平臺(tái),另外COM的序列化的原理利用了編譯器中虛表,使得其學(xué)習(xí)成本巨大。

  • CORBA是早期比較好的實(shí)現(xiàn)了跨平臺(tái),跨語言的序列化協(xié)議。COBRA的主要問題是參與方過多帶來的版本過多,版本之間兼容性較差,以及使用復(fù)雜晦澀。

  • XML & SOAP

    • XML是一種常用的序列化和反序列化協(xié)議,具有跨機(jī)器,跨語言等優(yōu)點(diǎn)。
    • SOAP(Simple Object Access protocol) 是一種被廣泛應(yīng)用的,基于XML為序列化和反序列化協(xié)議的結(jié)構(gòu)化消息傳遞協(xié)議。SOAP具有安全、可擴(kuò)展、跨語言、跨平臺(tái)并支持多種傳輸層協(xié)議。
  • JSON(JavaScript Object Notation)

    • 這種Associative array格式非常符合工程師對(duì)對(duì)象的理解。
    • 它保持了XML的人眼可讀(Human-readable)的優(yōu)點(diǎn)。
    • 相對(duì)于XML而言,序列化后的數(shù)據(jù)更加簡(jiǎn)潔。
    • 它具備javascript的先天性支持,所以被廣泛應(yīng)用于Web browser的應(yīng)用常景中,是Ajax的事實(shí)標(biāo)準(zhǔn)協(xié)議。
    • 與XML相比,其協(xié)議比較簡(jiǎn)單,解析速度比較快。
    • 松散的Associative array使得其具有良好的可擴(kuò)展性和兼容性。
  • Thrift是Facebook開源提供的一個(gè)高性能,輕量級(jí)RPC服務(wù)框架,其產(chǎn)生正是為了滿足當(dāng)前大數(shù)據(jù)量、分布式、跨語言、跨平臺(tái)數(shù)據(jù)通訊的需求。Thrift在空間開銷和解析性能上有了比較大的提升,對(duì)于對(duì)性能要求比較高的分布式系統(tǒng),它是一個(gè)優(yōu)秀的RPC解決方案;但是由于Thrift的序列化被嵌入到Thrift框架里面,Thrift框架本身并沒有透出序列化和反序列化接口,這導(dǎo)致其很難和其他傳輸層協(xié)議共同使用

  • Protobuf具備了優(yōu)秀的序列化協(xié)議的所需的眾多典型特征

    • 標(biāo)準(zhǔn)的IDL和IDL編譯器,這使得其對(duì)工程師非常友好。
    • 序列化數(shù)據(jù)非常簡(jiǎn)潔,緊湊,與XML相比,其序列化之后的數(shù)據(jù)量約為1/3到1/10。
    • 解析速度非常快,比對(duì)應(yīng)的XML快約20-100倍。
    • 提供了非常友好的動(dòng)態(tài)庫(kù),使用非常簡(jiǎn)介,反序列化只需要一行代碼。由于其解析性能高,序列化后數(shù)據(jù)量相對(duì)少,非常適合應(yīng)用層對(duì)象的持久化場(chǎng)景
  • Avro的產(chǎn)生解決了JSON的冗長(zhǎng)和沒有IDL的問題,Avro屬于Apache Hadoop的一個(gè)子項(xiàng)目。 Avro提供兩種序列化格式:JSON格式或者Binary格式。Binary格式在空間開銷和解析性能方面可以和Protobuf媲美,JSON格式方便測(cè)試階段的調(diào)試。適合于高性能的序列化服務(wù)。

  • 幾種協(xié)議的對(duì)比

    • XML序列化(Xstream)無論在性能和簡(jiǎn)潔性上比較差;
    • Thrift與Protobuf相比在時(shí)空開銷方面都有一定的劣勢(shì);
    • Protobuf和Avro在兩方面表現(xiàn)都非常優(yōu)越。

5、同步和異步

同步IO:

  • 讀寫IO時(shí)代碼等數(shù)據(jù)返回后才繼續(xù)執(zhí)行后續(xù)代碼
  • 代碼編寫簡(jiǎn)單,CPU執(zhí)行效率低
  • JDK提供的java.io是同步IO

異步IO:

  • 讀寫IO時(shí)僅發(fā)出請(qǐng)求,然后立即執(zhí)行后續(xù)代碼
  • 代碼編寫復(fù)雜,CPU執(zhí)行效率高
  • JDK提供的java.nio是異步IO

6、Java中的NIO,BIO,AIO分別是什么

  • 同步阻塞IO(BIO):用戶進(jìn)程發(fā)起一個(gè)IO操作以后,必須等待IO操作的真正完成后,才能繼續(xù)運(yùn)行;
  • 同步非阻塞IO(NIO):用戶進(jìn)程發(fā)起一個(gè)IO操作以后,可做其它事情,但用戶進(jìn)程需要經(jīng)常詢問IO操作是否完成,這樣造成不必要的CPU資源浪費(fèi);
  • 異步非阻塞IO(AIO):用戶進(jìn)程發(fā)起一個(gè)IO操作然后,立即返回,等IO操作真正的完成以后,應(yīng)用程序會(huì)得到IO操作完成的通知。類比Future模式。

  • 先來個(gè)例子理解一下概念,以銀行取款為例:
    • 同步 : 自己親自出馬持銀行卡到銀行取錢(使用同步IO時(shí),Java自己處理IO讀寫)。
    • 異步 : 委托一小弟拿銀行卡到銀行取錢,然后給你(使用異步IO時(shí),Java將IO讀寫委托給OS處理,需要將數(shù)據(jù)緩沖區(qū)地址和大小傳給OS(銀行卡和密碼),OS需要支持異步IO操作API)。
    • 阻塞 : ATM排隊(duì)取款,你只能等待(使用阻塞IO時(shí),Java調(diào)用會(huì)一直阻塞到讀寫完成才返回)。
    • 非阻塞 : 柜臺(tái)取款,取個(gè)號(hào),然后坐在椅子上做其它事,等號(hào)廣播會(huì)通知你辦理,沒到號(hào)你就不能去,你可以不斷問大堂經(jīng)理排到了沒有,大堂經(jīng)理如果說還沒到你就不能去(使用非阻塞IO時(shí),如果不能讀寫Java調(diào)用會(huì)馬上返回,當(dāng)IO事件分發(fā)器會(huì)通知可讀寫時(shí)再繼續(xù)進(jìn)行讀寫,不斷循環(huán)直到讀寫完成)。


BIO

定義:BIO 全稱Block-IO 是一種阻塞同步的通信模式。我們常說的Stock IO 一般指的是BIO。是一個(gè)比較傳統(tǒng)的通信方式,模式簡(jiǎn)單使用方便。但并發(fā)處理能力低通信耗時(shí)依賴網(wǎng)速

BIO 設(shè)計(jì)原理:

服務(wù)器通過一個(gè) Acceptor 線程負(fù)責(zé)監(jiān)聽客戶端請(qǐng)求和為每個(gè)客戶端創(chuàng)建一個(gè)新的線程進(jìn)行鏈路處理。典型的一請(qǐng)求一應(yīng)答模式。若客戶端數(shù)量增多,頻繁地創(chuàng)建和銷毀線程會(huì)給服務(wù)器打開很大的壓力。后改良為用線程池的方式代替新增線程,被稱為偽異步IO。

服務(wù)器提供IP地址和監(jiān)聽的端口,客戶端通過TCP的三次握手與服務(wù)器連接,連接成功后,雙放才能通過套接字(Stock)通信。

小結(jié):

BIO模型中通過 SocketServerSocket 完成套接字通道的實(shí)現(xiàn)。阻塞,同步,建立連接耗時(shí)。


為了改進(jìn)這種一連接一線程的模型,我們可以使用線程池來管理這些線程(需要了解更多請(qǐng)參考前面提供的文章),實(shí)現(xiàn)1個(gè)或多個(gè)線程處理N個(gè)客戶端的模型(但是底層還是使用的同步阻塞I/O),通常被稱為“偽異步I/O模型“。


實(shí)現(xiàn)很簡(jiǎn)單,我們只需要將新建線程的地方,交給線程池管理即可。

我們知道,如果使用 CachedThreadPool 線程池(不限制線程數(shù)量,如果不清楚請(qǐng)參考文首提供的文章),其實(shí)除了能自動(dòng)幫我們管理線程(復(fù)用),看起來也就像是1:1的客戶端:線程數(shù)模型,而使用 FixedThreadPool 我們就有效的控制了線程的最大數(shù)量,保證了系統(tǒng)有限的資源的控制,實(shí)現(xiàn)了N:M的偽異步 I/O 模型。

但是,正因?yàn)橄拗屏司€程數(shù)量,如果發(fā)生大量并發(fā)請(qǐng)求,超過最大數(shù)量的線程就只能等待,直到線程池中的有空閑的線程可以被復(fù)用。而對(duì) Socket 的輸入流就行讀取時(shí),會(huì)一直阻塞,直到發(fā)生:

  • 有數(shù)據(jù)可讀
  • 可用數(shù)據(jù)以及讀取完畢
  • 發(fā)生空指針或 I/O 異常

所以在讀取數(shù)據(jù)較慢時(shí)(比如數(shù)據(jù)量大、網(wǎng)絡(luò)傳輸慢等),大量并發(fā)的情況下,其他接入的消息,只能一直等待,這就是最大的弊端。

而后面即將介紹的NIO,就能解決這個(gè)難題。

NIO

NIO(官方:New IO),也叫Non-Block IO 是一種同步非阻塞的通信模式。

NIO 設(shè)計(jì)原理:

NIO相對(duì)于BIO來說一大進(jìn)步。客戶端和服務(wù)器之間通過Channel通信。NIO可以在Channel進(jìn)行讀寫操作。這些Channel都會(huì)被注冊(cè)在Selector多路復(fù)用器上。Selector通過一個(gè)線程不停的輪詢這些Channel。找出已經(jīng)準(zhǔn)備就緒的Channel執(zhí)行IO操作。
NIO 通過一個(gè)線程輪詢,實(shí)現(xiàn)千萬個(gè)客戶端的請(qǐng)求,這就是非阻塞NIO的特點(diǎn)。

1)緩沖區(qū)Buffer:它是NIO與BIO的一個(gè)重要區(qū)別。BIO是將數(shù)據(jù)直接寫入或讀取到Stream對(duì)象中。而NIO的數(shù)據(jù)操作都是在緩沖區(qū)中進(jìn)行的。緩沖區(qū)實(shí)際上是一個(gè)數(shù)組。Buffer最常見的類型是ByteBuffer,另外還有CharBuffer,ShortBuffer,IntBuffer,LongBuffer,FloatBuffer,DoubleBuffer。

2)通道Channel:和流不同,通道是雙向的。NIO可以通過Channel進(jìn)行數(shù)據(jù)的讀,寫和同時(shí)讀寫操作。通道分為兩大類:一類是網(wǎng)絡(luò)讀寫(SelectableChannel),一類是用于文件操作(FileChannel),我們使用的SocketChannel和ServerSocketChannel都是SelectableChannel的子類。

3)多路復(fù)用器Selector:NIO編程的基礎(chǔ)。多路復(fù)用器提供選擇已經(jīng)就緒的任務(wù)的能力。就是Selector會(huì)不斷地輪詢注冊(cè)在其上的通道(Channel),如果某個(gè)通道處于就緒狀態(tài),會(huì)被Selector輪詢出來,然后通過SelectionKey可以取得就緒的Channel集合,從而進(jìn)行后續(xù)的IO操作。服務(wù)器端只要提供一個(gè)線程負(fù)責(zé)Selector的輪詢,就可以接入成千上萬個(gè)客戶端,這就是JDK NIO庫(kù)的巨大進(jìn)步。

小結(jié):NIO模型中通過SocketChannel和ServerSocketChannel完成套接字通道的實(shí)現(xiàn)。非阻塞/阻塞,同步,避免TCP建立連接使用三次握手帶來的開銷。

AIO (NIO.2)

  • 異步非阻塞,服務(wù)器實(shí)現(xiàn)模式為一個(gè)有效請(qǐng)求一個(gè)線程,客戶端的I/O請(qǐng)求都是由OS先完成了再通知服務(wù)器應(yīng)用去啟動(dòng)線程進(jìn)行處理.
  • AIO方式使用于連接數(shù)目多且連接比較長(zhǎng)(重操作)的架構(gòu),比如相冊(cè)服務(wù)器,充分調(diào)用OS參與并發(fā)操作,編程比較復(fù)雜,JDK7開始支持。

AIO 并沒有采用NIO的多路復(fù)用器,而是使用異步通道的概念。其read,write方法的返回類型都是Future對(duì)象。而Future模型是異步的,其核心思想是:去主函數(shù)等待時(shí)間。

小結(jié):AIO模型中通過AsynchronousSocketChannel和AsynchronousServerSocketChannel完成套接字通道的實(shí)現(xiàn)。非阻塞,異步

總結(jié)

  • BIO模型中通過SocketServerSocket完成套接字通道實(shí)現(xiàn)。阻塞,同步,連接耗時(shí)。
  • NIO模型中通過SocketChannelServerSocketChannel完成套接字通道實(shí)現(xiàn)。非阻塞/阻塞,同步,避免TCP建立連接使用三次握手帶來的開銷。
  • AIO模型中通過AsynchronousSocketChannelAsynchronousServerSocketChannel完成套接字通道實(shí)現(xiàn)。非阻塞,異步。

  • 另外,I/O屬于底層操作,需要操作系統(tǒng)支持,并發(fā)也需要操作系統(tǒng)的支持,所以性能方面不同操作系統(tǒng)差異會(huì)比較明顯。

    參考:

    • Java BIO、NIO、AIO 學(xué)習(xí)-力量來源于赤誠(chéng)的愛!-51CTO博客
    • Netty序章之BIO NIO AIO演變 - JavaEE教程 - SegmentFault 思否
    • Java 網(wǎng)絡(luò)IO編程總結(jié)(BIO、NIO、AIO均含完整實(shí)例代碼) - CSDN博客
    • Java IO Tutorial

    7、BIO,NIO,AIO區(qū)別

    • BIO(同步阻塞):客戶端和服務(wù)器連接需要三次握手,使用簡(jiǎn)單,但吞吐量小
    • NIO(同步非阻塞):客戶端與服務(wù)器通過Channel連接,采用多路復(fù)用器輪詢注冊(cè)的Channel。提高吞吐量和可靠性。
    • AIO(異步非阻塞):NIO的升級(jí)版,采用異步通道實(shí)現(xiàn)異步通信,其read和write方法均是異步方法。

    8、Stock通信的偽代碼實(shí)現(xiàn)流程

  • 服務(wù)器綁定端口:server = new ServerSocket(PORT)
  • 服務(wù)器阻塞監(jiān)聽:socket = server.accept()
  • 服務(wù)器開啟線程:new Thread(Handle handle)
  • 服務(wù)器讀寫數(shù)據(jù):BufferedReader PrintWriter
  • 客戶端綁定IP和PORT:new Socket(IP_ADDRESS, PORT)
  • 客戶端傳輸接收數(shù)據(jù):BufferedReader PrintWriter
  • 9、網(wǎng)絡(luò)操作

    Java 中的網(wǎng)絡(luò)支持:

    • InetAddress:用于表示網(wǎng)絡(luò)上的硬件資源,即 IP 地址;
    • URL:統(tǒng)一資源定位符;
    • Sockets:使用 TCP 協(xié)議實(shí)現(xiàn)網(wǎng)絡(luò)通信;
    • Datagram:使用 UDP 協(xié)議實(shí)現(xiàn)網(wǎng)絡(luò)通信。

    InetAddress

    沒有公有構(gòu)造函數(shù),只能通過靜態(tài)方法來創(chuàng)建實(shí)例。

    InetAddress.getByName(String host); InetAddress.getByAddress(byte[] address);

    URL

    可以直接從 URL 中讀取字節(jié)流數(shù)據(jù)。

    public static void main(String[] args) throws IOException {URL url = new URL("http://www.baidu.com");// 字節(jié)流InputStream is = url.openStream();// 字符流InputStreamReader isr = new InputStreamReader(is, "utf-8");BufferedReader br = new BufferedReader(isr);String line = br.readLine();while (line != null) {System.out.println(line);line = br.readLine();}br.close(); }

    Sockets

    • ServerSocket:服務(wù)器端類
    • Socket:客戶端類
    • 服務(wù)器和客戶端通過 InputStream 和 OutputStream 進(jìn)行輸入輸出。


    參考資料:

    • 使用TCP/IP的套接字(Socket)進(jìn)行通信 - alps_01 - 博客園

    Datagram

    • DatagramPacket:數(shù)據(jù)包類
    • DatagramSocket:通信類

    什么是Socket?

    TCP用主機(jī)的IP地址加上主機(jī)上的端口號(hào)作為TCP連接的端點(diǎn),這種端點(diǎn)就叫做套接字(socket)或插口。

    套接字用(IP地址:端口號(hào))表示。

    Socket是進(jìn)程通訊的一種方式,即調(diào)用這個(gè)網(wǎng)絡(luò)庫(kù)的一些API函數(shù)實(shí)現(xiàn)分布在不同主機(jī)的相關(guān)進(jìn)程之間的數(shù)據(jù)交換。

    socket是網(wǎng)絡(luò)編程的基礎(chǔ),本文用打電話來類比socket通信中建立TCP連接的過程。

    socket函數(shù):表示你買了或者借了一部手機(jī)。
    bind函數(shù):告訴別人你的手機(jī)號(hào)碼,讓他們給你打電話。
    listen函數(shù):打開手機(jī)的鈴聲,而不是靜音,這樣有電話時(shí)可以立馬反應(yīng)。listen函數(shù)的第二個(gè)參數(shù),最大連接數(shù),表示最多有幾個(gè)人可以同時(shí)撥打你的號(hào)碼。不過我們的手機(jī),最多只能有一個(gè)人打進(jìn)來,要不然就提示占線。
    connect函數(shù):你的朋友知道了你的號(hào)碼,通過這個(gè)號(hào)碼來聯(lián)系你。在他等待你回應(yīng)的時(shí)候,不能做其他事情,所以connect函數(shù)是阻塞的。
    accept函數(shù):你聽到了電話鈴聲,接電話,accept it!然后“喂”一聲,你的朋友聽到你的回應(yīng),知道電話已經(jīng)打進(jìn)去了。至此,一個(gè)TCP連接建立了。
    read/write函數(shù):連接建立后,TCP的兩端可以互相收發(fā)消息,這時(shí)候的連接是全雙工的。對(duì)應(yīng)打電話中的電話煲。
    close函數(shù):通話完畢,一方說“我掛了”,另一方回應(yīng)"你掛吧",然后將連接終止。實(shí)際的close(sockfd)有些不同,它不止是終止連接,還把手機(jī)也歸還,不在占有這部手機(jī),就當(dāng)是公用電話吧。

    注意到,上述連接是阻塞的,你一次只能響應(yīng)一個(gè)用戶的連接請(qǐng)求,但在實(shí)際網(wǎng)絡(luò)編程中,一個(gè)服務(wù)器服務(wù)于多個(gè)客戶,上述方案也就行不通了,怎么辦?想一想10086,移動(dòng)的聲訊服務(wù)臺(tái),也是只有一個(gè)號(hào)碼,它怎么能同時(shí)服務(wù)那么多人呢?可以這樣理解,在你打電話到10086時(shí),總服務(wù)臺(tái)會(huì)讓一個(gè)接線員來為你服務(wù),而它自己卻繼續(xù)監(jiān)聽有沒有新的電話接入。在網(wǎng)絡(luò)編程中,這個(gè)過程類似于fork一個(gè)子進(jìn)程,建立實(shí)際的通信連接,而主進(jìn)程繼續(xù)監(jiān)聽。10086的接線員是有限的,所以當(dāng)連接的人數(shù)達(dá)到上線時(shí),它會(huì)放首歌給你聽,忙等待,直到有新的空閑接線員為止。
    實(shí)際網(wǎng)絡(luò)編程中,處理并發(fā)的方式還有select/poll/epoll等。

    下面是一個(gè)實(shí)際的socket通信過程:


    Socket的特點(diǎn)

  • Socket基于TCP鏈接,數(shù)據(jù)傳輸有保障
  • Socket適用于建立長(zhǎng)時(shí)間鏈接
  • Socket編程通常應(yīng)用于即時(shí)通訊
  • 轉(zhuǎn)載于:https://www.cnblogs.com/chung567115/p/9765281.html

    總結(jié)

    以上是生活随笔為你收集整理的Java修炼之道--I/O的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 午夜激情影院 | 打美女屁股网站 | 午夜91| 亚洲精品男女 | 精品乱码一区二区三四区视频 | 天天干天天做天天操 | www视频在线观看 | 亚洲综合不卡 | av在线不卡网站 | 性开放的欧美大片 | 欧美一区综合 | 久久精品国产露脸对白 | 久久伊人网站 | 久久精品午夜福利 | 邻居少妇张开双腿让我爽一夜 | 免费h片在线观看 | 加勒比波多野结衣 | 久久黄页 | 伊人久久大香线蕉av一区 | 一区二区三区在线免费观看视频 | 国产精品4p | 国产伦精品一区二区三区视频网站 | 深爱婷婷网 | 大地资源高清播放在线观看 | 二区三区视频 | 久操国产| 可以在线观看的av网站 | 亚洲国产精品影院 | 亚洲二级片 | 超污巨黄的小短文 | 国产精品一区二区三区四区 | 黄色一及毛片 | 视频一区中文字幕 | 天天色天天爽 | 国产一区二区免费在线 | 日本污网站 | 天天色天天干天天色 | 久久精品视频一区 | 久久国内偷拍 | 在线免费观看黄 | 中国毛片在线观看 | 91精品播放 | 曰韩在线 | 中文字幕一区二区三区久久久 | 免费网站在线观看人数在哪动漫 | 理论视频在线观看 | 魔女鞋交玉足榨精调教 | eeuss鲁丝片一区二区三区 | 久久久亚洲综合 | 永久免费毛片 | 少妇激情一区二区三区 | 国产a级免费视频 | 亚洲色图国产视频 | 97人人模人人爽人人少妇 | 俄罗斯丰满熟妇hd | 国产噜噜噜噜噜久久久久久久久 | 日本r级电影在线观看 | 国产精品视频一区二区三区在3 | 一起射导航 | 亚洲精品合集 | 成人一区二区免费视频 | 91久久久久一区二区 | 五月天激情综合网 | 久久亚洲私人国产精品va | 日本丰满少妇一区二区三区 | 日韩电影一区 | 污片网址| 麻豆成人网 | 99思思 | 久久国产亚洲 | 中文字幕在线观 | 久久这里有精品视频 | www好男人 | 国产成人91 | 成人免费毛片片v | wwwav在线播放 | 男女羞羞无遮挡 | 黄视频在线免费看 | 国产主播一区二区三区 | 女人久久久久 | 国产成a人亚洲精v品在线观看 | 人妻精品久久久久中文字幕 | 欧美成人国产 | 91视频最新地址 | 天天爽天天爽 | 日本三级韩国三级三级a级中文 | 骚狐网站| 日本黄页网址 | 性做爰裸体按摩视频 | 欧美亚洲色综久久精品国产 | 伊人影院av| 欧美视频日韩 | 人人射人人爱 | 免费精品视频一区二区三区 | 人妻少妇精品无码专区 | 国产片91 | 日本特黄视频 | 亚洲制服一区二区 | 精品国产a线一区二区三区东京热 |