先看下下面代码先
static class Thread1 extends Thread {
@Override
public void run() {
try {
InputStream in = System.in;
OutputStream out = System.out;
int i = 0;
while ((i = in.read()) != -1) {
out.write(i);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
这段代码很正常,线程跑起来的结果和你想的一样,在终端上输入一行,按下回车就回显这一行,显然System.in是行缓冲的(linebuffered),不是像getch那样输入一个就回显一个。在没有按下回车之前in.read()就一直阻塞。 再来看下这一段代码
static class Thread2 extends Thread {
@Override
public void run() {
try {
InputStream in = System.in;
OutputStream out = System.out;
byte buffer[] = new byte[10];
int i = 0;
while ((i=in.read(buffer))!=-1) {
out.write(buffer, 0, i);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Thread2跑起来结果跟Thread1一模一样,输入一行回显一行,但代码却有点区别,多了byte buffer[] = new byte[10];
这是一个缓冲,读取的时候不是一次一个字节了,而是in.read(buffer)
,这个方法等同于in.read(buffer, 0, buffer.length)
,也就是说要先把buffer读满,read方法才会结束。然后我困惑了,Thread2运行起来竟然Thread1一模一样。如果你输入abc然后回车,Thread2也会立刻回显abc。这时候的字节数只有4还没把buffer填满,但in.read(buffer)却返回了,我参考了文档对InputStream中read(byte[]
b, int off, int len) 的说明:
…将输入流中最多 len 个数据字节读入字节数组。尝试读取多达 len 字节,但可能读取较少数量。以整数形式返回实际读取的字节数。 在输入数据可用、检测到流的末尾或者抛出异常前,此方法一直阻塞。…
前一句又「尝试”又”可能”的,太模糊暂时忽略,第二提出了不阻塞的情况,看似除了「输入数据可用」理解有点模糊外,其他都明显不符合,那理应阻塞的方法是什么原因让它提前返回呢?带着这个疑问,我看了下InputStream的源码,并在Thread3中实现了一个一模一样的方法(如文档所说的,就是反复调用read()):
static class Thread3 extends Thread {
static int read(InputStream in, byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c;
c= in.read();
if (c == -1) {
return -1;
}
b[off] = (byte) c;
int i = 1;
try {
for (; i < len; i++) {
c = in.read();
if (c == -1) {
break;
}
b[off + i] = (byte) c;
}
} catch (IOException ee) {
}
return i;
}
@Override
public void run() {
try {
InputStream in = System.in;
OutputStream out = System.out;
byte buffer[] = new byte[10];
int i = 0;
while ((i=read(in,buffer,0,buffer.length))!=-1) {
out.write(buffer, 0, i);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Thread3跑起来果然跟Thread2不一样,算是比较可理解了,不管你按下多少次回车,只有但buffer读满了,read(in,buffer,0,buffer.length)才会返回。当然这并不是说明System.in有什么问题,System.in必须是InputStream的子类,read(byte[] b, int off, int len) 被重载,必须的。我只是好奇它到底是怎么实现的,在Thread4我做了一些尝试:
static class Thread4 extends Thread {
static int read(InputStream in, byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c;
c= in.read();
if (c == -1) {
return -1;
}
b[off] = (byte) c;
int i = 1;
try {
for (; i < len; i++) {
if (in.available() > 0) {
c = in.read();
if (c == -1) {
break;
}
} else {
break;
}
b[off + i] = (byte) c;
}
} catch (IOException ee) {
}
return i;
}
@Override
public void run() {
try {
InputStream in = System.in;
OutputStream out = System.out;
byte buffer[] = new byte[10];
int i = 0;
while ((i=read(in,buffer,0,buffer.length))!=-1) {
out.write(buffer, 0, i);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
在in.available()永远有效且准确的前提下这个方法就跟System.in一样了,但我觉得这个前提很无力啊,但实际System.in它的实现是怎样的呢,还有为什么?
希望能在深入理解计算机系统找到答案,
最近看开始这本书,太棒了,才看第一章就对它入迷了,大学时代与它失之交臂真是遗憾啊。。。。啊。。。