Java BIO

同步與異步,阻塞與非阻塞

同步:當前線程發起了一個調用或請求,而後當前線程須要等待該調用結束返回結果才能繼續往下進行其餘操做。java

異步:當前線程發起了一個調用或請求,而後當前線程不需等待調用的執行結果就能夠繼續往下執行(請求交由另外一個線程去執行),以後能夠經過被調用者的狀態改變或者被調用者主動發出通知來得到執行結果。數組

阻塞:也能夠說是等待,調用者發起請求若是不能當即拿到返回結果就一直等,等到結果完成。緩存

非阻塞:調用者發起請求以後當即返回,沒有等待的過程,可是要本身不斷去檢查是否已有結果。網絡

同步異步講的是消息通訊機制,關注點側重於線程與線程之間的協做。而阻塞、非阻塞更側重於調用者在等待結果時的狀態。併發

Java中的同步IO:線程A發起了一個IO請求,那麼就由java程序去執行io操做,而且是由當前線程去作。dom

Java中的異步IO就是當前線程把IO請求委託給了另一個線程去作。異步

Java中的阻塞IO就是線程A發起了IO請求,若是此時所需IO資源被佔用,那麼線程A就要被掛起,等待其餘線程釋放IO資源分配給了A,A才能執行IO操做。學習

Java中的非阻塞IO是指線程A發起了一個IO請求,一樣IO資源被佔用,可是A不被掛起,而是馬上返回,以後每隔一段時間再去看看IO資源是否空閒。this

BIO

BIO是java最先期的IO,位於java.io包下,它主要面向的對象是流。編碼

分類

IO流是用來處理設備之間的數據傳輸,是有起點和終點的字節結合。按數據傳輸方向可劃分爲輸入流、輸出流——相對於內存設備而言,將外設中的數據讀取到當前程序中爲輸入,將程序中的數據寫出到外設中爲輸出。

按是否阻塞劃分爲阻塞流、非阻塞流

按操做的數據對象能夠劃分爲字節流、字符流——字符流其實是字節流+編碼表,即字節流讀取文字字節數據後不直接操做而是先查對應的編碼表,獲取對應的文字,再對這個文字進行操做,所以能夠說傳統的IO都是基於字節Byte的,簡稱BIO。

四個頂級抽象父類

  • 字節輸入流:InputStream
  • 字節輸出流:OutputStream
  • 字符輸入流:Reader
  • 字符輸出流:Writer

IO體系

分類 字節輸入流 字節輸出流 字符輸入流 字符輸出流
抽象基類 InputStream OutputStream Reader Writer
訪問文件 FileInputStream FileOutputStream FileReader FileWriter
訪問字符串 StringReader StringWriter
緩衝流 Buffered<br/>InputStream Buffered<br/>OutputStream BufferedReader BuffferedWriter
轉換流 InputStreamReader OutputStreamWriter
對象流 ObjectInputStream ObjectOutputStream
打印流 PrintStream PrintWriter
推回輸入流 PushbackInputStream PushBackReader
特殊流 DataInputStream DataOutputStream
訪問數組 ByteArray<br/>InputStream ByteArray<br/>OutputStream CharArray<br/>Reader CharArray<br/>Writer
訪問管道流 Piped<br/>InputStream Piped<br/>OutputStream Piped<br/>Reader Peped<br/>Writer

其餘經常使用類

File:File類是對文件系統中的文件和文件夾進行抽象的對象,經過File類能夠訪問文件或文件夾的各類數據信息和操做,好比文件名、文件長度、最後修改時間、是否可讀、獲取路徑、判斷是否存在、建立和刪除文件、目錄。

RandomAccessFile:RandomAccessFile封裝了字節流,同時還封裝了一個字符數組緩衝區,經過內部的指針去操做字符數組中的數據。

java.io 範圍

java.io 包主要涉及標準輸入輸出、錯誤輸出(System.in,System.out,System.error)、文件、管道、網絡數據流、內存緩存等的輸入輸出。 主要學習各個類的設計目的和使用場景。

流的概念

流從概念上來講是一個連續的數據流,不能經過索引讀寫數據,只能順序訪問流中的數據。流又能夠根據讀寫單位粒度不一樣分爲字節流和字符流,BIO是經過從流中讀取數據,往流中寫入數據從而達到數據源與目的媒介的關聯。

java.io不經常使用字節、字符流

管道流

管道:Java中的管道是爲了讓運行在同一個JVM中的兩個線程之間能進行通訊的一種手段,經過管道通訊的雙方應該是運行在同一個進程中的不一樣線程,而在Unix/Linux中的管道概念是能讓運行在不一樣地址空間的進程經過管道通訊。

管道流示例 要點:輸入流要關聯輸出流;相關聯的流要在不一樣線程使用。

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class PipeStreamDemo {

	PipedOutputStream out;
	PipedInputStream in;

	/**
	 * 管道流示例: Java中的管道流只能用於同一個進程中的不一樣線程通訊, 一個管道輸入流必須關聯一個管道輸出流,
	 * 一個線程往管道輸出流中寫數據,另外一個線程往管道輸入流中取數據。
	 * 
	 * @throws IOException
	 */
	public void PipeDemo() throws IOException {
		out = new PipedOutputStream();
		in = new PipedInputStream(out);// 一個輸入流要跟一個輸出流關聯
		// 相關聯的管道輸入流和輸出流必須在不一樣線程使用,
		// 由於read和write方法會致使流阻塞,在同一個線程會致使死鎖
		// 如下兩個線程的啓動順序無關
		new Thread(() -> {
			try {
				out.write("this is a pipe stream demo".getBytes());
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}).start();

		new Thread(() -> {
			try {
				byte[] result = new byte[1024];
				int b = in.read(result);
				while (b != -1) {
					System.out.println(new String(result));
					b = in.read(result);
				}
				in.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}).start();
	}

	public static void main(String[] args) throws IOException {
		PipeStreamDemo demo = new PipeStreamDemo();
		demo.PipeDemo();
	}
}

標準流

System.in、System.out、System.err是三個常見的標準流,使用得最多的是System.out在控制檯程序裏將輸出打印到控制檯。這三個對象是在JVM啓動時就完成了初始化,程序中能夠直接使用。

System.out與System.err最主要的區別是System.out是能夠被重定向的系統流,而System.err不能被重定向,使用System.setIn()和System.setOut()能夠重定向System.in和System.out。

併發問題

在同一時刻不能有多個線程同時從InputStream或者Reader中讀取數據,也不能同時往OutputStream或者Writer裏寫數據。由於沒法保證每一個線程讀取多少數據,以及多個線程寫數據時的順序。若是能保證操做的順序,那麼使用同一個stream、reader、writer則是可行的。

RandomAccessFile

RandomAccessFile能夠來回讀寫文件,也能夠替換文件中的某些部分。經過RandomAccessFile實例的seek(int) 方法能將文件指針移動到指定位置,從而在此處開始進行讀寫。

字節數組流

ByteArrayInputStream和ByteArrayOutputStream是用來讀寫字節數組的流對象。

推回輸入流 PushbackInputStream

PushbackInputStream用於解析InputStream中的數據,有時候咱們須要提早知道接下來將要讀取到的字節內容,才能判斷用何種方式進行數據解析,PushbackInputStream容許將讀取到的字節從新推回到InputStream中,以便再次經過read()讀取。示例代碼以下:

PushbackInputStream in = new PushbackInputStream(new FileInputStream(file));
int data = in.read();
//do something
in.unread(data); //將上一次讀取到的data個字節推回流中

順序輸入流SequenceInputStream

SequenceInputStream把一個或者多個InputStream整合起來,造成一個邏輯連貫的輸入流。當讀取SequenceInputStream時,會先從第一個輸入流中讀取,完成以後再從第二個輸入流讀取,依次推類。

InputStream input1 = new FileInputStream(file);
InputStream input2 = new FileInputStream(file2);
InputStream combined = new SequenceInputStream(input1, input2);

LineNumberReader

LineNumberReader是記錄了已讀取數據行號的BufferedReader。默認狀況下,行號從0開始,當LineNumberReader讀取到行終止符時,行號會遞增。 LinuNumberReader的setLineNumber()僅僅改變LineNumberReader內的記錄行號的變量值,不會改變當前流的讀取位置。流的讀取依然是順序進行,意味着你不能經過setLineNumber()實現流的跳躍讀取。

分詞流 StreamTokenizer

StreamTokenizer能夠把InputStream和Reader分解成一系列符號,好比將英語句子分解成一個個單詞。

StringReader sr = new StringReader("How old are you? I am 20.");
StreamTokenizer tokenizer = new StreamTokenizer(sr);

while (tokenizer.nextToken() != StreamTokenizer.TT_EOF) { //流末尾
    if (tokenizer.ttype == StreamTokenizer.TT_WORD) {
        System.out.println(tokenizer.sval); //字符串類型
    } else if (tokenizer.ttype == StreamTokenizer.TT_NUMBER) {
        System.out.println(tokenizer.nval); //數字類型
    } else if (tokenizer.ttype == StreamTokenizer.TT_EOL) { //行末尾
        System.out.println("exit!");
    } 
}
相關文章
相關標籤/搜索