1. 程式人生 > >JAVA的節點流和處理流以及流的關閉順序

JAVA的節點流和處理流以及流的關閉順序

今天在編寫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 等-提供將基礎資料型別寫入到檔案中,或者讀取出來.

流的關閉順序

  1. 一般情況下是:先開啟的後關閉,後開啟的先關閉
  2. 另一種情況:看依賴關係,如果流a依賴流b,應該先關閉流a,再關閉流b。例如,處理流a依賴節點流b,應該先關閉處理流a,再關閉節點流b
  3. 可以只關閉處理流,不用關閉節點流。處理流關閉的時候,會呼叫其處理的節點流的關閉方法。

注意:

  1. 如果將節點流關閉以後再關閉處理流,會丟擲IO異常。
  2. 如果關閉了處理流,在關閉與之相關的節點流,也可能出現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;  
}