JAVA的節點流和處理流以及流的關閉順序
阿新 • • 發佈:2018-12-14
今天在編寫hadoop程式的時候,用到了流的處理。關閉流的時候出現了問題:
程式碼:
1 FSDataInputStream fsin = fs.open(new Path(filein)); 2 FSDataOutputStream fsout = fs.append(new Path(fileout)); 3 BufferedReader br = new BufferedReader(new InputStreamReader(fsin)); 4 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fsout)); 5 ... 6 br.close(); 7 fsin.close(); 8 bw.close(); 9 fsout.close();
異常:
1 [[email protected] bigdata]# ./bigdataTimer.sh 2 INFO [main] com.sinosoft.bigdata.BigDataTimer.main(227) | -----Timer begin----- 3 INFO [main] com.sinosoft.bigdata.BigDataInit.<clinit>(70) | bigdata init ... 4 WARN [main] org.apache.hadoop.util.NativeCodeLoader.<clinit>(62) | Unable to load native-hadoop library for your platform... using builtin-java classes where applicable 5 INFO [main] com.sinosoft.bigdata.BigDataTimer.mergeFile(66) | Finished appending file. 6 INFO [main] com.sinosoft.bigdata.BigDataTimer.mergeFile(68) | deleted the input data path. 7 INFO [main] com.sinosoft.bigdata.BigDataTimer.mergeFile(70) | create the input data path. 8 ERROR [main] org.apache.hadoop.hdfs.DFSClient.closeAllFilesBeingWritten(776) | Failed to close file /user/root/dayfileInput/dayfileIn 9 org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.hdfs.server.namenode.LeaseExpiredException): No lease on /user/root/dayfileInput/dayfileIn: File does not exist. Holder DFSClient_NONMAPREDUCE_-221972347_1 does not have any open files.
原因:關閉處理流br.close();之後,緊接著關閉了與之相關的fsin.close();的節點流。事實上,br.close();會呼叫fsin.close(); 因此重複關閉了2次fsin.close();最後丟擲了異常。
回顧了一下流的知識:
按照流是否直接與特定的地方(如磁碟、記憶體、裝置等)相連,分為節點流和處理流兩類。
- 節點流:可以從或向一個特定的地方(節點)讀寫資料。如FileReader.
- 處理流:是對一個已存在的流的連線和封裝,通過所封裝的流的功能呼叫實現資料讀寫。如BufferedReader.處理流的構造方法總是要帶一個其他的流物件做引數。一個流物件經過其他流的多次包裝,稱為流的連結。
JAVA常用的節點流:
- 文 件 FileInputStream FileOutputStrean FileReader FileWriter 檔案進行處理的節點流。
- 字串 StringReader StringWriter 對字串進行處理的節點流。
- 數 組 ByteArrayInputStream ByteArrayOutputStreamCharArrayReader CharArrayWriter 對陣列進行處理的節點流(對應的不再是檔案,而是記憶體中的一個數組)。
- 管 道 PipedInputStream PipedOutputStream PipedReaderPipedWriter對管道進行處理的節點流。
常用處理流(關閉處理流使用關閉裡面的節點流)
- 緩衝流:BufferedInputStrean BufferedOutputStream BufferedReader BufferedWriter
---增加緩衝功能,避免頻繁讀寫硬碟。
- 轉換流:InputStreamReader OutputStreamReader實現位元組流和字元流之間的轉換。
- 資料流 DataInputStream DataOutputStream 等-提供將基礎資料型別寫入到檔案中,或者讀取出來.
流的關閉順序
- 一般情況下是:先開啟的後關閉,後開啟的先關閉
- 另一種情況:看依賴關係,如果流a依賴流b,應該先關閉流a,再關閉流b。例如,處理流a依賴節點流b,應該先關閉處理流a,再關閉節點流b
- 可以只關閉處理流,不用關閉節點流。處理流關閉的時候,會呼叫其處理的節點流的關閉方法。
注意:
- 如果將節點流關閉以後再關閉處理流,會丟擲IO異常。
- 如果關閉了處理流,在關閉與之相關的節點流,也可能出現IO異常。(hadoop程式設計檔案流操作中遇到了。)
另一篇:
1.在finally中關閉流;
OutputStream out = null; try { out = new FileOutputStream(""); // ...操作流程式碼 } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) { out.close(); } } catch (Exception e) { e.printStackTrace(); } }
2.在關閉多個流時因為嫌麻煩將所有關流的程式碼丟到一個try中
OutputStream out = null; OutputStream out2 = null; try { out = new FileOutputStream(""); out2 = new FileOutputStream(""); // ...操作流程式碼 } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) { out.close();// 如果此處出現異常,則out2流也會被關閉 } } catch (Exception e) { e.printStackTrace(); } try { if (out2 != null) { out2.close(); } } catch (Exception e) { e.printStackTrace(); } }
3.在迴圈中建立流,在迴圈外關閉,導致關閉的是最後一個流
for (int i = 0; i < 10; i++) { OutputStream out = null; try { out = new FileOutputStream(""); // ...操作流程式碼 } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) { out.close(); } } catch (Exception e) { e.printStackTrace(); } } }
4.在Java7中,關閉流這種繁瑣的操作就不用我們自己寫了
只要實現的自動關閉介面(Closeable)的類都可以在try結構體上定義,java會自動幫我們關閉,及時在發生異常的情況下也會。可以在try結構體上定義多個,用分號隔開即可,如:
try (OutputStream out = new FileOutputStream("");OutputStream out2 = new FileOutputStream("")){ // ...操作流程式碼 } catch (Exception e) { throw e; }