如果你也是年輕的程序員,關注我一起學習探討
最近在工作中遇到了一個Android的ANR問題,經過分析是WiFiStateMachine調用了系統函數readline(),然后出現了阻塞的現象,然后就深入了解了一下readline函數。網上搜了一下,發現關於readline()函數的解釋大都是說readline()函數是阻塞函數,沒有消息是並不會返回null,而是一直阻塞在那。至於阻塞的實質,都沒有涉及,我經過仔細分析源碼,得出結論如下:
我們先看readline函數源碼,其中fill()函數才是真正讀取數據的地方,只有讀取完成之后,才會執行下面“/n”“/r”的判斷,而讀取數據的時候為什么阻塞,之前並沒有人探究,那我們后面看看fill函數。
String readLine(boolean ignoreLF) throws IOException {
StringBuffer s = null;
int startChar;
synchronized (lock) {
ensureOpen();
boolean omitLF = ignoreLF || skipLF;
bufferLoop:
for (;;) {
if (nextChar >= nChars)
fill(); //真正讀取數據的地方
if (nextChar >= nChars) { /* EOF */
if (s != null && s.length() > 0)
return s.toString();
else
return null;
}
...........其他
charLoop:
for (i = nextChar; i < nChars; i++) {
c = cb[i];
if ((c == '\n') || (c == '\r')) { //判斷是否遇到\n或者\r,如果遇到就結束,作為一行數據輸出。
eol = true;
break charLoop;
}
}
fill()函數中,可以看到讀取數據是用了read(char[] cbuf, int off, int len) 來讀取數據,當沒有數據時,n值為0,while循環就不會結束,就會一直在此阻塞。
private void fill() throws IOException {
int dst;
if (markedChar <= UNMARKED) {
/* No mark */
dst = 0;
} else {
..............其他
int n;
do {
n = in.read(cb, dst, cb.length - dst);
} while (n == 0); //阻塞的關鍵
if (n > 0) {
nChars = dst + n;
nextChar = dst;
}
}
總結:
1、讀入的數據要注意有/r或/n或/r/n
2、沒有數據時會阻塞,在數據流異常或斷開時才會返回null
3、readline()函數不會邊讀邊輸出,而是有一個緩沖區,讀出的數據先放到緩沖區,遇到/r或/n或/r/n后再輸出。
源碼路徑:xref: /libcore/ojluni/src/main/java/java/io/BufferedReader.java