Java BufferedOutputStream類的常用方法講解
BufferedOutputStream類的常用方法
BufferedOutputStream字節(jié)緩沖輸出流
構(gòu)造方式
第一種開(kāi)發(fā)中
public BufferedOutputStream(OutputStream out)
采用的默認(rèn)的緩沖區(qū)大小(足夠大了) ,來(lái)構(gòu)造一個(gè)字節(jié)緩沖輸出流對(duì)象
public BufferedOutputStream(OutputStream out,int size)
指定size緩沖區(qū)大小構(gòu)造緩沖輸出流對(duì)象
IllegalArgumentException - 如果 size <= 0
常用方法
public void write(int b)throws IOException
一次寫一個(gè)字節(jié)
- b - 要寫入的字節(jié)。
public void write(byte[] b,int off,int len) throws IOException
一次寫一個(gè)字節(jié)數(shù)組的一部分
- b - 數(shù)據(jù)。
- off - 數(shù)據(jù)的起始偏移量。
- len - 要寫入的字節(jié)數(shù)。
public void flush() throws IOException
刷新此緩沖的輸出流。這迫使所有緩沖的輸出字節(jié)被寫出到底層輸出流中。
public void close() throws IOException
關(guān)閉此輸出流并釋放與此流有關(guān)的所有系統(tǒng)資源。
FilterOutputStream 的 close 方法先調(diào)用其 flush 方法,然后調(diào)用其基礎(chǔ)輸出流的 close 方法。
程序示例
public static void main(String[] args) throws Exception { //符合Java一種設(shè)計(jì)模式:裝飾者設(shè)計(jì)模式(過(guò)濾器:Filter) BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt")) ; //寫數(shù)據(jù) bos.write("hello".getBytes()); //釋放資源 bos.close(); }
BufferedOutputStream深入分析
FileOutputStream和BufferedOutputStream都提供了一系列的將數(shù)據(jù)寫入文件的方式,并且我們都知道BufferedOutputStream要比直接使用FileOutputStream寫入速度要快,本文通過(guò)案例實(shí)際演示一下兩者的區(qū)別。
代碼準(zhǔn)備
public class BufferFile { public static void main(String[] args) { //每次向文件中寫入一個(gè)8字節(jié)的數(shù)組 byte[] bytes = "1234567\n".getBytes(); //每隔100毫秒通過(guò)buffer的方式向文件中寫入數(shù)據(jù) new Thread(() -> { System.out.println("buffer_while start..."); File file = new File("/var/file_test_data/out_buffer_while.txt"); FileOutputStream fileOutputStream; try { fileOutputStream = new FileOutputStream(file); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); while (true) { Thread.sleep(100); bufferedOutputStream.write(bytes); } } catch (Exception e) { e.printStackTrace(); } }).start(); //通過(guò)buffer的方式向文件中寫入1千萬(wàn)次 new Thread(() -> { System.out.println("buffer_for start..."); File file = new File("/var/file_test_data/out_buffer_for.txt"); FileOutputStream fileOutputStream; try { fileOutputStream = new FileOutputStream(file); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); for (int i = 0; i < 10000000; i++) { bufferedOutputStream.write(bytes); } } catch (Exception e) { e.printStackTrace(); } System.out.println(new Date() + ": buffer_for end..."); }).start(); //通過(guò)file的方式向文件中寫入1千萬(wàn)次 new Thread(() -> { System.out.println("file_for start..."); File file = new File("/var/file_test_data/out_file_for.txt"); FileOutputStream fileOutputStream; try { fileOutputStream = new FileOutputStream(file); for (int i = 0; i < 10000000; i++) { fileOutputStream.write(bytes); } } catch (Exception e) { e.printStackTrace(); } System.out.println(new Date() + ": file_for end..."); }).start(); } }
開(kāi)始運(yùn)行
強(qiáng)行停止后的運(yùn)行結(jié)果
1、file和buffe寫入速度比較
兩者分別寫入1千萬(wàn)次,時(shí)間上buffer比f(wàn)ile快8秒,如果當(dāng)寫入次數(shù)指數(shù)級(jí)增加時(shí),buffer的優(yōu)勢(shì)將更加明顯。
2、數(shù)據(jù)寫入完整性問(wèn)題
buffer雖然要比f(wàn)ile快,但是從最終數(shù)據(jù)上可以看出,buffer會(huì)丟數(shù)據(jù)
- 當(dāng)?shù)谝粋€(gè)線程寫入時(shí)數(shù)據(jù)還未滿8kb時(shí),強(qiáng)制停止java進(jìn)程,最終out_buffer_while.txt沒(méi)有數(shù)據(jù)。
- 第二個(gè)線程,雖然最終代碼執(zhí)行完畢,但是比較file方式,out_buffer_for.txt文件看起來(lái)也丟了一部分?jǐn)?shù)據(jù)。
原因分析
當(dāng)使用buffer讀寫文件時(shí),數(shù)據(jù)并沒(méi)有直接被寫入磁盤,而是被緩存到一個(gè)字節(jié)數(shù)據(jù)中,這個(gè)字節(jié)數(shù)組的大小是8kb,默認(rèn)情況下只有當(dāng)8kb被填充滿了以后,數(shù)據(jù)才會(huì)被一次性寫入磁盤,這樣一來(lái)就大大減少了系統(tǒng)調(diào)用的次數(shù)(file是每一次write都會(huì)產(chǎn)生系統(tǒng)調(diào)用),當(dāng)然也正是因?yàn)閎uffer中的每一次write只是寫入到內(nèi)存中(JVM自身內(nèi)存中),所以當(dāng)數(shù)據(jù)未寫入磁盤前,如果JVM進(jìn)程掛了,那么就會(huì)造成數(shù)據(jù)丟失。
手動(dòng)刷盤
為了解決數(shù)據(jù)丟失的問(wèn)題,buf中提供了flush()方法,用戶可以自行決定合適將數(shù)據(jù)刷寫到磁盤中
- 如果你的flush()調(diào)用的非常頻繁,那就會(huì)退化為普通的file模式了。
- 如果你的flush()調(diào)用的又不太頻繁,那么丟數(shù)據(jù)的可能性就比較高。
- 無(wú)論如何業(yè)務(wù)邏輯中數(shù)據(jù)寫完時(shí),一定要調(diào)用一次flush(),確保緩沖區(qū)的數(shù)據(jù)刷到磁盤上。
將無(wú)限循環(huán)寫入的代碼注釋掉,在buf寫1千萬(wàn)完成后,加上bufferedOutputStream.flush();
public class BufferFile { public static void main(String[] args) { //每次向文件中寫入一個(gè)8字節(jié)的數(shù)組 byte[] bytes = "1234567\n".getBytes(); //每隔100毫秒通過(guò)buffer的方式向文件中寫入數(shù)據(jù) /*new Thread(() -> { System.out.println("buffer_while start..."); File file = new File("/var/file_test_data/out_buffer_while.txt"); FileOutputStream fileOutputStream; try { fileOutputStream = new FileOutputStream(file); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); while (true) { Thread.sleep(100); bufferedOutputStream.write(bytes); } } catch (Exception e) { e.printStackTrace(); } }).start();*/ //通過(guò)buffer的方式向文件中寫入1千萬(wàn)次 new Thread(() -> { System.out.println("buffer_for start..."); File file = new File("/var/file_test_data/out_buffer_for.txt"); FileOutputStream fileOutputStream; try { fileOutputStream = new FileOutputStream(file); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); for (int i = 0; i < 10000000; i++) { bufferedOutputStream.write(bytes); } bufferedOutputStream.flush(); } catch (Exception e) { e.printStackTrace(); } System.out.println(new Date() + ": buffer_for end..."); }).start(); //通過(guò)file的方式向文件中寫入1千萬(wàn)次 new Thread(() -> { System.out.println("file_for start..."); File file = new File("/var/file_test_data/out_file_for.txt"); FileOutputStream fileOutputStream; try { fileOutputStream = new FileOutputStream(file); for (int i = 0; i < 10000000; i++) { fileOutputStream.write(bytes); } } catch (Exception e) { e.printStackTrace(); } System.out.println(new Date() + ": file_for end..."); }).start(); } }
這次再看數(shù)據(jù)寫入完整了
buffer源碼分析
類的機(jī)構(gòu)圖
首先當(dāng)創(chuàng)建一個(gè)BufferedOutputStream對(duì)象時(shí),構(gòu)造方法就初始化了緩沖的字節(jié)數(shù)組大小為8kb
protected byte buf[]; public BufferedOutputStream(OutputStream out) { this(out, 8192); } public BufferedOutputStream(OutputStream out, int size) { super(out); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; }
當(dāng)調(diào)用buffer.write(b)時(shí),調(diào)用的是父類FilterOutputStream的方法
public void write(byte b[]) throws IOException { //寫入的字節(jié)數(shù)組b,從0開(kāi)始,一共要寫入的長(zhǎng)度 write(b, 0, b.length); } public void write(byte b[], int off, int len) throws IOException { if ((off | len | (b.length - (len + off)) | (off + len)) < 0) throw new IndexOutOfBoundsException(); //遍歷數(shù)組,一個(gè)字節(jié)一個(gè)字節(jié)的把數(shù)據(jù)寫入數(shù)組中 for (int i = 0 ; i < len ; i++) { write(b[off + i]); } } public synchronized void write(int b) throws IOException { //判斷字節(jié)長(zhǎng)度是否超過(guò)buf.length,buf在初始化已經(jīng)指定大小為8192,即8kb //如果超過(guò)則調(diào)用flushBuffer if (count >= buf.length) { flushBuffer(); } 把每一個(gè)字節(jié)寫入緩沖的buf數(shù)組中,并且統(tǒng)計(jì)值count++ buf[count++] = (byte)b; } private void flushBuffer() throws IOException { if (count > 0) { //真正的調(diào)用OutputStream,寫入數(shù)據(jù)到磁盤中 //寫入buf緩沖字節(jié)數(shù)組數(shù)據(jù),從0下標(biāo)開(kāi)始,一直寫到count,即有多少寫多少。 out.write(buf, 0, count); count = 0; } }
關(guān)于buf緩沖數(shù)據(jù)大小設(shè)置
buffer提供了可以自定義緩沖大小的構(gòu)造方法
public BufferedOutputStream(OutputStream out, int size) { super(out); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; }
如果緩沖大小設(shè)置的比較大。
- 好處:進(jìn)一步減少調(diào)用系統(tǒng)內(nèi)核寫數(shù)據(jù)的方法,提高寫入速度,kafka的批寫入默認(rèn)就是16kb寫一次。
- 壞處:1、丟失的數(shù)據(jù)可能會(huì)更多,2、要注意堆內(nèi)存的消耗。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
在Spring Boot中集成RabbitMQ詳細(xì)步驟(最新推薦)
本文將介紹如何在Spring Boot項(xiàng)目中集成RabbitMQ,實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者的基本配置,本文分步驟給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-12-12用Java產(chǎn)生100個(gè)1-150間不重復(fù)數(shù)字
這篇文章主要介紹了用Java產(chǎn)生100個(gè)1-150間不重復(fù)數(shù)字,需要的朋友可以參考下2017-02-02SpringCloud集成Eureka并實(shí)現(xiàn)負(fù)載均衡的過(guò)程詳解
這篇文章主要給大家詳細(xì)介紹了SpringCloud集成Eureka并實(shí)現(xiàn)負(fù)載均衡的過(guò)程,文章通過(guò)代碼示例和圖文講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的參考價(jià)值,需要的朋友可以參考下2023-11-11SpringCloud+Tornado基于jwt實(shí)現(xiàn)請(qǐng)求安全校驗(yàn)功能
這篇文章主要介紹了SpringCloud+Tornado基于jwt實(shí)現(xiàn)請(qǐng)求安全校驗(yàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12springmvc前臺(tái)向后臺(tái)傳值幾種方式總結(jié)(從簡(jiǎn)單到復(fù)雜)
今天小編就為大家分享一篇springmvc前臺(tái)向后臺(tái)傳值幾種方式總結(jié)(從簡(jiǎn)單到復(fù)雜),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08JAVAWEB實(shí)現(xiàn)簡(jiǎn)單的商城項(xiàng)目(一)實(shí)例代碼解析
本文給大家分享一段實(shí)例代碼給大家介紹JAVAWEB實(shí)現(xiàn)簡(jiǎn)單的商城項(xiàng)目(一),非常具有參考價(jià)值,感興趣的朋友一起學(xué)習(xí)吧2016-02-02