java的I/O操做類在包java.io下,大體分紅4組:前端
全部文件的存儲都是字節(byte)的儲存,在磁盤上保留的並非文件的字符而是先把字符編碼成字節,再存儲這些字節到磁盤。在讀取文件時,也是一個字節一個字節讀取。java
字節流能夠用於任何類型的對象,包括二進制對象;而字符流只能處理字符或者字符串linux
字節流是最基本的,全部的InputStrem和OutputStream的子類都是,主要用在處理二進制數據,它是按字節來處理的。ios
但實際中不少的數據是文本,又提出了字符流的概念,它是按虛擬機的encode來處理,也就是要進行字符集的轉化。程序員
字符和字節流這兩個之間經過 InputStreamReader,OutputStreamWriter來關聯,其實是經過byte[]和String來關聯 在實際開發中出現的漢字問題實際上都是在字符流和字節流之間轉化不統一而形成的。web
2.1 java的I/O類庫的基本架構數據庫
2.1.1 基於字節的I/O操做接口後端
InputStream和OutputStream設計模式
inputStream的read()返回int:0到255範圍內的int字節值。若是到達末尾而沒有可用的字節,返回-1.所以不能用於0-255數組
來表示的值就得用字符流來讀取。
2.1.2 基於字符的I/O操做接口
無論是磁盤仍是網絡傳輸,最小的存儲單元是字節,而不是字符,因此I/O操做的都是字節而不是字符。
可是爲何有操做字符的I/O接口呢?這是由於程序中常常操做的數據都是字符形式的,爲了操做方便固然要提供一個直接寫字符的I/O接口。
從字符到字節必須通過編碼轉換,而編碼又耗時,編碼還容易出現亂碼。
Writer類提供了一個抽象方法write(char cbuf[], int off, int len).
讀字符的接口是int read(char cbuf[], int off, in len): 返回的int:做爲整數讀取的字符(16位,2個字節),範圍是0到65535,若是已經到流的末尾,
則返回-1
2.1.3 字節與字符的轉化接口
數據持久化或網絡傳輸都是以字節進行的,因此必需要有從字符到字節或從字節到字符的轉化。
InputStreamReader類:從字節到字符轉化的橋樑, 從inputStream到reader的過程是指定編碼字符集,不然將採用操做系統默認的字符集,極可能出現亂碼問題。
StreamDecoder正是完成從字節到字符的解碼的實現類。
補充:IO部分介紹不詳細,此處補充一些IO的知識
補充1:若是須要建立文件須要如下操做:判斷映射的文件是否真實存在fie.existes() ,爲true就存在,不然不存在。
若是文件不存在要調用file.createdNewFile() 建立真實文件,可是,這個方法只是會建立文件自己,若是文件的目錄不存在,則不會建立目錄。因此須要對父文件存在與否左判斷。
File parent = file.getParentFile() // 獲取父文件
if(!parent.exists()) parent.mkdirs(); // 建立父文件夾。
補充二:FileInputStream是有緩衝區的,因此用完以後必須關閉,不然可能致使內存佔滿,數據丟失。
int count = 0; InputStream streamReder = null; try { streamReder = new FileInputStream(new File("folder1/fsdfsdf/fsdffd/fileName4.txt")); while(streamReder.read() != -1) // 每次讀一個字節,返回的是字節的int表示0 ~ 255 { count++; } System.out.println("---長度是: "+count+" 字節");
補充三:
OutputStream out=new FileOutputStream("D:/David/Java/java 高級進階/files/tiger2.jpg");
out.write(buffer, 0, numberRead); //不然會自動被填充0
補充四:ObjectInputStream和ObjectOutputStream
可是要實現這種功能,被讀取和寫入的類必須實現Serializable接口
補充五:字符流
FileWriter是被修飾者
BufferedWriter 是修飾者
BufferedWriter bw = new BufferedWriter(new FileWriter("file name"));
上面這個BufferedWriter加了一個緩衝,緩衝寫滿後再將數據寫入硬盤,這樣極大的提升性能。必需要有bw.flush()這句,若是沒有,而僅有writer.close(),會報異常。
若是單獨使用FileWriter也能夠,每寫一個數據,硬盤就有一個寫動做,性能差。
OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(f)); BufferedWriter writer = new BufferedWriter(write); writer.write(content); writer.flush(); write.close(); writer.close();
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
FileReader fk=new FileReader("f:/23.txt"); //從F盤讀取一個文件fk接收
BufferedReader bk=new BufferedReader(fk); //生成一個容器bk把文件fk內容裝進去,這樣bk的內容就是原文件23.txt的內容了
從定義上看可能會讓你感到困惑,這裏解釋一下:輸入輸出是相對於內存設備而言的
補充六:
沒有明確指定須要使用的字符編碼方案時,Java程序經過「java.nio.charset.Charset.defaultCharset().name()」語句來獲取默認的字符編碼方案,該語句返回的值跟運行Java程序的操做系統的設置有關,在有些操做系統上,該語句返回值多是UTF-8;在有些操做系統上,該語句返回值多是GBK;在有些操做系統上,該語句返回值多是除了UTF-8和GBK之外的其餘字符編碼方案。這樣子,程序的可移植性大大下降。
編碼只發生在JVM和底層操做系統(以及網絡傳輸)之間進行數據傳輸時,若是程序中沒有IO操做,那麼全部的String和Char都以unicode編碼。當從磁盤讀取文件或者往磁盤寫入文件時使用的編碼要一致,也就是編碼和解碼使用的字符集要同樣纔不會出現亂碼
Unicode :又稱萬國碼,顧名思義,unicode中收錄了世界各國語言,用以解決傳統編碼的侷限性。它爲每種語言中的每一個字符設定了統一而且惟一的二進制編碼,以知足跨語言、跨平臺進行文本轉換、處理的要求。
UTF-8:(8-bit Unicode Transformation Format)是一種針對Unicode的可變長度字符編碼.
一、不一樣編碼表示一個字符所佔用字節是不相同的,其中ASCII碼佔一個字節,GB23十二、GBK、unicode都用2字節表示一個字符。而UTF-8是可變長的,英文字母用1字節,漢字用3字節表示。
二、同一個字符在不一樣編碼表中的位置也是不一樣的,好比漢字‘中’在GBK中是D6D0,而在unicode中是4E2D。也就致使了當漢字的解碼方式和編碼方式不一樣時會產生亂碼。
每一個Java程序員都應該記住,Java使用的是Unicode編碼。全部的字符在JVM中(內存中)只有一個存在形式就是Unicode。因此一個char佔用2字節。
Java的IO體系中面向字符的IO類只有Reader和Writer,可是最經常使用的FileReader和FileWriter類不支持自定義編碼類型,只能使用系統默認編碼。這樣一來,讀寫文件的編碼就必定一致了,也就減小了亂碼的可能性。我的理解,這麼作多是強制幫助用戶完成編碼一致,下降亂碼率。若是要自定義編碼,要用其父類InputStreamRreader和OutputStreamWriter
補充七: JAVA中幾種類型的流?JDK爲每種類型的流提供了一些抽象類以供繼承,請說出他們分別是什麼?
字節流和字符流。
字節流和字符流區別?
計算機中的一切最終都是二進制的字節形式存在。對於「中國」這些字符,首先要獲得其對應的字節,而後將字節寫入到輸出流。讀取時,首先讀到的是字節,但是咱們要把它顯示爲字符,咱們須要將字節轉換成字符。因爲這樣的需求很普遍,人家專門提供了字符流的包裝類。
底層設備永遠只接受字節數據,有時候要寫字符串到底層設備,須要將字符串轉成字節再進行寫入。字符流是字節流的包裝,字符流則是直接接受字符串,它內部將串轉成字節,再寫入底層設備,這爲咱們向IO設別寫入或讀取字符串提供了一點點方便
什麼是JAVA序列化?如何實現序列化?
補充八:Unicode 和 UTF-8區別
String newStr = new String(oldStr.getBytes(), "UTF-8");
java中的String類是按照unicode進行編碼的,當使用String(byte[] bytes, String encoding)構造字符串時,encoding所指的是bytes中的數據是按照那種方式編碼的,而不是最後產生的String是什麼編碼方式,換句話說,是讓系統把bytes中的數據由encoding編碼方式轉換成unicode編碼。若是不指明,bytes的編碼方式將由jdk根據操做系統決定。
// 打開文件時,指定編碼方式。
InputStreamReader read = new InputStreamReader (new FileInputStream(f),"UTF-8");
FileReader讀取文件的過程當中,FileReader繼承了InputStreamReader,但並無實現父類中帶字符集參數的構造函數,因此FileReader只能按系統默認的字符集來解碼。
用InputStreamReader代替FileReader,InputStreamReader isr=new InputStreamReader(new FileInputStream(fileName),"UTF-8");這樣讀取文件就會直接用UTF-8解碼,不用再作編碼轉換。
// 指定編碼方式 BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(saveFilename),"GB2312")); PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(saveFilename),"GB2312")));
補充九:
發現:全部的字節相關的都是以Stream爲結尾; 全部的字符相關的都是以writer/reader爲結尾。
以字節爲導向的stream------InputStream/OutputStream。
InputStream 和 OutputStream是兩個abstact類,對於字節爲導向的stream都擴展這兩個雞肋(基類^_^);
--InputStream
ByteArrayInputStream - 把內存中的一個緩衝區做爲InputStream使用。
StringBufferInputStream - 把一個String對象做爲InputStream(不推薦使用) .
FileInputStream -- 把一個文件做爲InputStream,實現對文件的讀取操做
PipedInputStream - 實現了pipe的概念,主要在線程中使用. 管道輸入流是指一個通信管道的接收端。
一個線程經過管道輸出流發送數據,而另外一個線程經過管道輸入流讀取數據,
這樣可實現兩個線程間的通信。
SequenceInputStream - 把多個InputStream合併爲一個InputStream .「序列輸入流」類容許應用程序把幾個輸入流連續地合併起來,
而且使它們像單個輸入流同樣出現。每一個輸入流依次被讀取,直到到達該流的末尾。
而後「序列輸入流」類關閉這個流並自動地切換到下一個輸入流。
--OutputSteam
ByteArrayOutputStream - 把信息存入內存中的一個緩衝區中.該類實現一個以字節數組形式寫入數據的輸出流。
FileOutputStream:文件輸出流是向 File 或 FileDescriptor 輸出數據的一個輸出流。
PipedOutputStream:管道輸出流是指一個通信管道的發送端。 一個線程經過管道輸出流發送數據,
而另外一個線程經過管道輸入流讀取數據,這樣可實現兩個線程間的通信。
以字符爲導向的stream Reader/Writer
以Unicode字符爲導向的stream,表示以Unicode字符爲單位從stream中讀取或往stream 中寫入信息。 Reader/Writer 爲abstact類 以Unicode字符爲導向的stream包括下面幾種類型:
-- Reader
1: CharArrayReader:與ByteArrayInputStream對應
2: StringReader:與StringBufferInputStream對應
3: FileReader: 與FileInputStream對應
4: PipedReader: 與PipedInputStream對應
-- Writer:
1) CharArrayWrite:與ByteArrayOutputStream對應
2) StringWrite:無與之對應的以字節爲導向的stream
3) FileWrite:與FileOutputStream對應
4) PipedWrite:與PipedOutputStream對應
補充十:字符流和字節流之間轉
InputStreamReader和OutputStreamReader:把一個以字節爲導向的stream轉換成一個以字符爲導向的stream。
一個 InputStreamReader 類是從字節流到字符流的橋樑:它讀入字節,並根據指定的編碼方式,將之轉換爲字符流。
使用的編碼方式可能由名稱指定,或平臺可接受的缺省編碼方式。
InputStreamReader 的 read() 方法之一的每次調用,可能促使從基本字節輸入流中讀取一個或多個字節。
爲了達到更高效率,考慮用 BufferedReader 封裝 InputStreamReader,
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
InputStreamReader(InputStream) 用缺省的字符編碼方式,建立一個 InputStreamReader。
InputStreamReader(InputStream, String) 用已命名的字符編碼方式,建立一個 InputStreamReader。
OutputStreamWriter 將多個字符寫入到一個輸出流,根據指定的字符編碼將多個字符轉換爲字節。
每一個 OutputStreamWriter 合併它本身的 CharToByteConverter, 於是是從字符流到字節流的橋樑。
補充十一:Java IO的通常使用原則
1、按數據來源(去向)分類:
一、是文件: FileInputStream, FileOutputStream, FileReader, FileWriter
二、是byte[]:ByteArrayInputStream, ByteArrayOutputStream
三、是Char[]: CharArrayReader, CharArrayWriter
四、是String: StringBufferInputStream(不推薦), StringReader, StringWriter
五、網絡數據流:InputStream, OutputStream, Reader, Writer
2、按是否格式化輸出分:
一、要格式化輸出:PrintStream, PrintWriter
3、按是否要緩衝分:
一、要緩衝:BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter
4、按數據格式分:
一、二進制格式(只要不能肯定是純文本的): InputStream, OutputStream及其全部帶Stream結束的子類
二、純文本格式(含純英文與漢字或其餘編碼方式);Reader, Writer及其全部帶Reader, Writer的子類
5、按輸入輸出分:
一、輸入:Reader, InputStream類型的子類
二、輸出:Writer, OutputStream類型的子類
6、特殊須要:
一、從Stream到Reader,Writer的轉換類:InputStreamReader, OutputStreamWriter
二、對象輸入輸出:ObjectInputStream, ObjectOutputStream
三、進程間通訊:PipeInputStream, PipeOutputStream, PipeReader, PipeWriter
四、合併輸入:SequenceInputStream
五、更特殊的須要:PushbackInputStream, PushbackReader, LineNumberInputStream, LineNumberReader
補充十二:幾個經常使用類的用法
FileInputStream :在java中,可使用InputStream對文件進行讀取,就是字節流的輸入。
創建合適大小的byte數組,若是已知輸入流的大小
File f = new File("E:"+File.separator+"java2"+File.separator+"StreamDemo"+File.separator+"test.txt"); InputStream in = new FileInputStream(f); byte b[]=new byte[(int)f.length()]; //建立合適文件大小的數組 in.read(b); //讀取文件中的內容到b[]數組 in.close();
若是不知輸入流的大小,則確定須要創建一個很大的byte數組,那麼byte中極可能有空的內容,那麼如何正確合適的將byte數組的中的內容輸出:
File f = new File("E:"+File.separator+"java2"+File.separator+"StreamDemo"+File.separator+"test.txt"); InputStream in = new FileInputStream(f); byte b[] = new byte[1024]; int len = 0; int temp=0; //全部讀取的內容都使用temp接收 while((temp=in.read())!=-1){ //當沒有讀取完時,繼續讀取 b[len]=(byte)temp; len++; } in.close();
FileOutputStream:
try{ // 構造一個要寫的數據: byte[] data = "這個例子測試文件寫".getBytes("GB2312"); FileOutputStream fo = new FileOutputStream("MyOut1.data"); fo.write(data); //fo.flush(); //若是OutputStream 的實現使用了緩存,這個方法用於清空緩存裏的數據,並通知底層去進行實際的寫操做 // FileOutputStream 沒有使用緩存,所以這個方法調用與否在這個例子的運行結果沒有影響。 fo.close(); }catch(Exception e) { e.printStackTrace(); }
BufferedInputStream是帶緩衝區的輸入流,默認緩衝區大小是8M,可以減小訪問磁盤的次數,提升文件讀取性能;BufferedOutputStream是帶緩衝區的輸出流,可以提升文件的寫入效率。BufferedInputStream與BufferedOutputStream分別是FilterInputStream類和FilterOutputStream類的子類,實現了裝飾設計模式。
OutputStream out = 38 new BufferedOutputStream( //是一種裝飾模式 39 new FileOutputStream(file), 16); 40 41 // 將ArrayLetters數組的前10個字節寫入到輸出流中 42 out.write(ArrayLetters, 0, 10); 43 // 將「換行符\n」寫入到輸出流中 44 out.write('\n'); 45 46 // TODO! 47 //out.flush(); 51 out.close();
字符流:可是最經常使用的FileReader和FileWriter類不支持自定義編碼類型,只能使用系統默認編碼。這樣一來,讀寫文件的編碼就必定一致了,也就減小了亂碼的可能性。我的理解,這麼作多是強制幫助用戶完成編碼一致,下降亂碼率。若是要自定義編碼,要用其父類InputStreamRreader和OutputStreamWriter
// 聲明一個File對象 File file = new File("hellowolrd.txt"); // 聲明一個Write對象 Writer writer = null; // 經過FileWriter類來實例化Writer類的對象並以追加的形式寫入 writer = new FileWriter(file, true); // 聲明一個要寫入的字符串 String str = "字符串形式寫入Helloworld"; // 寫入文本文件中 writer.write(str); // 刷新 writer.flush(); // 關閉字符輸出流 writer.close(); // 聲明一個File對象 File file = new File("hellowolrd.txt"); // 聲明一個Reader類的對象 Reader reader = null; // 經過FileReader子類來實例化Reader對象 reader = new FileReader(file); // 聲明一個字符數組 char[] c = new char[1024]; // // 將內容輸出 // int len = reader.read(c); //循環方式一個一個讀 int len=0; int temp=0; while((temp=reader.read())!=-1){ c[len]=(char)temp; len++; } // 關閉輸入流 reader.close();
字符流讀取、寫入文件:
package ioTest; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.util.Scanner; public class CharRWStreamTest01 { public static void main(String[] args) { File file=new File("Text.txt"); File file2=new File("Text1.txt"); try { InputStream fis=new FileInputStream(file); // 定義字節流 InputStreamReader isr=new InputStreamReader(fis, "utf-8"); // 用字節流做爲字符流的輸入,可是要指定編碼方式
這裏InputStreamReader是由字節流到字符流的橋樑
BufferedReader br=new BufferedReader(isr); // 裝飾模式 單緩存 OutputStream os=new FileOutputStream(file2,false); OutputStreamWriter osw=new OutputStreamWriter(os, "utf-8"); BufferedWriter bw=new BufferedWriter(osw); String ss; while((ss=br.readLine())!=null){ bw.write(ss); bw.write("\n"); } bw.flush(); br.close(); isr.close(); fis.close(); bw.close(); osw.close(); os.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
2.2 磁盤I/O工做機制
2.2.1 幾種訪問文件的方式
讀取和寫入文件IO,都調用OS提供的接口,磁盤由OS管理,應用程序要訪問物理設備只能經過系統調用的方式來工做。讀和寫分別對應read() 和 write()。 而系統調用就涉及到內核空間地址和用戶空間地址切換的問題,這是OS爲了保護系統安全,把用戶程序空間和內核空間隔離形成的。雖然保證了安全,可是必然存在數據須要從內核空間向用戶空間複製的問題。
磁盤I/O操耗時,數據從磁盤複製到內核空間,再複製到用戶空間,很是緩慢。OS爲了加速I/O操做,在內核空間使用緩存機制,將從磁盤讀取的文件按照必定的組織方式緩存,若是用戶程序訪問的是同一段磁盤地址的空間數據,那麼OS將從內核緩存中直接取出返回給用戶程序,減小I/O響應時間。
1 標準訪問文件的方式
讀和寫都是針對內核的高速緩存。
2 直接I/O的方式
直接訪問磁盤數據。如在數據庫管理系統中,由應用程序控制哪些熱點數據須要緩存,而不是OS。對熱點數據能夠進行預加載。
3 同步訪問文件的方式
數據的讀取和寫入是同步的,與標準文件的訪問方式不一樣的是 只有數據被成功寫到磁盤時才返回給應用程序成功標誌。
4 異步訪問文件的方式
5 內存映射的方式
2.2.2 Java 訪問磁盤文件
如何將數據持久化到物理磁盤?
2.2.3 Java 序列化技術
將一個對象轉化成一串二進制表示的字節數組,經過保存和轉義這些字節數據來達到持久化的目的。
須要持久化一個對象,對象必須繼承java.io.Serializable接口。
反序列化是相反的過程,將字節數組再從新構形成對象。反序列化時,必須有原始類做爲模板,才能將對象還原。
序列化總結:
SerialVersionUID的做用:
簡單來講,Java的序列化機制是經過在運行時判斷類的serialVersionUID來驗證版本一致性的。在進行反序列化時,JVM會把傳來的字節流中的serialVersionUID與本地相應實體(類)的serialVersionUID進行比較,若是相同就認爲是一致的,能夠進行反序列化,不然就會出現序列化版本不一致的異常。(InvalidCastException)
2.3 網絡I/O工做機制
2.3.1 TCP狀態轉化
(1) CLOSED狀態
(2) Listen:Server端在等待鏈接的狀態,Server爲此要調用socket, bind, listen函數,進入這個狀態。這稱爲應用程序被打開(等待客戶端來連
接。)
(3) SYN-SENT: 有客戶端要創建鏈接,通過三次握手創建。
(4) 關閉的時候:四次握手關閉。
2.3.2 影響網絡傳輸的因素
網絡帶寬 、 傳輸距離、 TCP阻塞控制(TCP緩衝區大小)
2.3.3 Java Socket的工做機制
socket底層用的TCP/UDP協議。 socket經過綁定端口號,能夠惟一肯定一個主機上應用程序的通訊鏈路
2.3.4 創建通訊鏈路
當客戶端要與服務器端通訊時,客戶端首先建立一個Socket實例,OS爲這個socket建立一個沒有使用過的本地端口號,並建立一個包含本地地址、遠程地址和端口號的套接字,在socket構造函數正確返回以前,要進行TCP三次握手,握手完成後,socket實例對象被建立完成。
服務器端將建立一個ServerSocket實例,建立ServerSocket比較簡單,只要指定的端口號沒有被佔用,通常都成功(同時會bind指定端口號,開始listen)。以後調用accept()方法時, 進入阻塞狀態,等待客戶端請求。當有新的請求到來時,將爲這個鏈接創建一個新的套接字數據結構,該套接字數據結構包含地址和端口信息。注意:這時服務端與之對應的socket實例並無完成建立,要等到3次握手完成後,這個服務器端的socket纔會返回。
2.3.5 數據傳輸
當鏈接建立完後,服務端和客戶端都會擁有一個socket實例,每一個socket實例都有一個inputstreamf和outputstream, 用這兩個對象交換數據。
網路傳輸的是字節流,當建立socket對象時,OS會爲inputstream和outputstream分配緩衝區,數據讀寫經過這個緩衝區來完成的。
2.4 NIO的工做方式
對一個文件描述符指定的文件或設備, 有兩種工做方式: 阻塞 與非阻塞 。所謂阻塞方式的意思是指, 當試圖對該文件描述符進行讀寫時, 若是當時沒有東西可讀,或者暫時不可寫, 程序就進入等待 狀態, 直到有東西可讀或者可寫爲止。而對於非阻塞狀態, 若是沒有東西可讀, 或者不可寫, 讀寫函數立刻返回, 而不會等待
2.4.1 BIO帶來的挑戰
BIO就是阻塞式I/O,不管是磁盤I/O仍是網絡I/O,數據在寫入OutputStream或者從InputStream讀取時均可能會阻塞,一旦有阻塞,線程會失去CPU的使用權,這在當前大規模訪問量和有性能要求的狀況下是不能接受的。雖然當前網絡I/O有一些解決辦法,如一個客戶端對應一個處理線程,出現阻塞時只是影響一個線程,不影響其餘線程,還有爲了減小系統線程的開銷,採用線程池的辦法減小線程建立和回收的成本。
可是在一些場景下仍然是沒法解決的,好比一些須要大量HTTP長鏈接的狀況,像淘寶如今使用的Web旺旺,服務端須要同時保持幾百萬HTTP鏈接,並非每時每刻這些鏈接都在傳輸數據,在這種狀況下不可能建立那麼多的線程保持鏈接。
就算線程數量不是問題,也存在以下問題:好比想給一些客戶更高的優先級時,很難經過設計線程的優先級來完成;另外每一個客戶端的請求在服務端可能須要訪問一些競爭資源,由於客戶端在不一樣的線程,所以須要同步。
2.4.2 NIO的工做機制
2.4.3 Buffer的工做方式
2.4.4 NIO的數據訪問方式
1.FileChannel.transferXXX
2. FileChannel.map
2.5 I/O調優
2.5.1 磁盤I/O優化
1. 性能檢測
iostat : linux 下查看 iO的一些參數,I/O wait參數不該該操過25%。
iops: 每秒IO的讀寫次數。 是磁盤的性能指標。
raid: 磁盤冗餘陣列。
2. 提高I/O性能
增長緩存,減小磁盤訪問次數;
優化磁盤管理系統,設計最優的磁盤方式策略,以及磁盤的尋址策略,這是在底層OS層面考慮的;
設計合理的磁盤存儲數據塊,以及訪問這些數據塊的策略,這是從應用層面考慮。例如 咱們能夠給存放的數據設計索引,經過尋址索引加快和減小磁盤的訪問量,還能夠採用異步和非 阻塞的方式加快磁盤的訪問速度(具體方法好比,給數據庫建索引,減小IO; NIO技術);
應用合理的RAID策略提高磁盤IO;
2.5.2 TCP網絡參數調優
增大端口數目, 減小TIME_WAIT(能夠快速釋放請求) , 除此以外,還可讓TCP複用等。
echo "1024 65535" > /proc/sys/net/ipv4/ip_local_port_range設置向外鏈接可用端口範圍 表示可使用的端口爲65535-1024個(0~1024爲受保護的)
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse 設置time_wait鏈接重用 默認0
echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle 設置快速回收time_wait鏈接 默認0
echo 180000 > /proc/sys/net/ipv4/tcp_max_tw_buckets 設置最大time_wait鏈接長度 默認262144
echo 1 > /proc/sys/net/ipv4/tcp_timestamps 設置是否啓用比超時重發更精確的方法來啓用對RTT的計算 默認0
echo 1 > /proc/sys/net/ipv4/tcp_window_scaling 設置TCP/IP會話的滑動窗口大小是否可變 默認1
echo 20000 > /proc/sys/net/ipv4/tcp_max_syn_backlog 設置最大處於等待客戶端沒有應答的鏈接數 默認2048
echo 15 > /proc/sys/net/ipv4/tcp_fin_timeout 設置FIN-WAIT狀態等待回收時間 默認60
echo "4096 87380 16777216" > /proc/sys/net/ipv4/tcp_rmem 設置最大TCP數據發送緩衝大小,分別爲最小、默認和最大值 默認4096 87380 4194304
echo "4096 65536 16777216" > /proc/sys/net/ipv4/tcp_wmem 設置最大TCP數據 接受緩衝大小,分別爲最小、默認和最大值 默認4096 87380 4194304
echo 10000 > /proc/sys/net/core/somaxconn 設置每個處於監聽狀態的端口的監聽隊列的長度 默認128
echo 10000 > /proc/sys/net/core/netdev_max_backlog 設置最大等待cpu處理的包的數目 默認1000
echo 16777216 > /proc/sys/net/core/rmem_max 設置最大的系統套接字數據接受緩衝大小 默認124928
echo 262144 > /proc/sys/net/core/rmem_default 設置默認的系統套接字數據接受緩衝大小 默認124928
echo 16777216 > /proc/sys/net/core/wmem_max 設置最大的系統套接字數據發送緩衝大小 默認124928
echo 262144 > /proc/sys/net/core/wmem_default 設置默認的系統套接字數據發送緩衝大小 默認124928
echo 2000000 > /proc/sys/fs/file-max 設置最大打開文件數 默認385583
結合ab命令來壓測機器優化網絡
設置完記得保存
2.5.3 網絡I/O優化
減小網絡交互的次數:減小交互次數一般是在網絡兩端設置緩存,如Oracle的JDBC驅動程序就提供了對SQL結果的緩存,有效減小對數據庫的訪問。
還有一招,合併訪問請求,好比查十回,每次查一個;也能夠一次查10個;
訪問的靜態資源好比JS,CSS,可已經多個JS文件名稱合併在一個HTTP請求中,發送web後端,後端根據URL 把JS文件打包一併返回給前端;
減小網絡傳輸數據量的大小:數據壓縮在傳輸;
儘可能經過讀取協議頭來獲取有用信息,儘可能避免讀取通訊數據body來得到須要的信息。
儘可能減小編碼:減小字節和字符的轉。
1. 同步與異步: 異步沒法保證依賴,須要在可靠性和性能之間作平衡(作五星的時候,遇到過這個問題,加購物車,扣庫存。)
2. 阻塞與非阻塞:主要從CPU消耗上來講的,阻塞就是CPU停下來等待一個慢的操做;非阻塞就是慢操做執行時,CPU去作其餘事;
非阻塞的方式雖然提升CPU利用率 可是線程切換增長;
3. 兩種方式的組合:
2.6 設計模式解析之適配器模式
2.6.1 適配器模式的結構
2.6.2 Java I/O中的適配器模式
2.7 設計模式之裝飾器模式
2.7.1 裝飾器模式的結構
2.7.2 Java I/O中的裝飾器模式
2.8 適配器模式與裝飾器模式的區別