多线程java IO之管道流

转】http://blog.csdn.net/javatiger427/article/details/5357827java

1、Java管道流

要在文本框中显示控制台输出,咱们必须用某种方法“截取”控制台流。换句话说,咱们要有一种高效地读取写入到System.out和System.err 全部内容的方法。若是你熟悉Java的管道流PipedInputStream和PipedOutputStream,就会相信咱们已经拥有最有效的工具。

写入到PipedOutputStream输出流的数据能够从对应的PipedInputStream输入流读取。Java的管道流极大地方便了咱们截取控制台输出。Listing 1显示了一种很是简单的截取控制台输出方案。

【Listing 1:用管道流截取控制台输出】

PipedInputStream pipedIS = new PipedInputStream();
PipedOutputStream pipedOS = new PipedOutputStream();
try {
   pipedOS.connect(pipedIS);
}
catch(IOException e) {
   System.err.println("链接失败");
   System.exit(1);
}
PrintStream ps = new PrintStream(pipedOS);
System.setOut(ps);
System.setErr(ps);
能够看到,这里的代码极其简单。咱们只是创建了一个PipedInputStream,把它设置为全部写入控制台流的数据的最终目的地。全部写入到控制台流的数据都被转到PipedOutputStream,这样,从相应的PipedInputStream读取就能够迅速地截获全部写入控制台流的数据。接下来的事情彷佛只剩下在Swing JTextArea中显示从pipedIS流读取的数据,获得一个可以在文本框中显示控制台输出的程序。遗憾的是,在使用Java管道流时有一些重要的注意事项。只有认真对待全部这些注意事项才能保证Listing 1的代码稳定地运行。下面咱们来看第一个注意事项。

1.1 注意事项一
PipedInputStream运用的是一个1024字节固定大小的循环缓冲区。写入PipedOutputStream的数据实际上保存到对应的 PipedInputStream的内部缓冲区。从PipedInputStream执行读操做时,读取的数据实际上来自这个内部缓冲区。若是对应的 PipedInputStream输入缓冲区已满,任何企图写入PipedOutputStream的线程都将被阻塞。并且这个写操做线程将一直阻塞,直至出现读取PipedInputStream的操做从缓冲区删除数据。

这意味着,向PipedOutputStream写数据的线程不该该是负责从对应PipedInputStream读取数据的惟一线程。从图二能够清楚地看出这里的问题所在:假设线程t是负责从PipedInputStream读取数据的惟一线程;另外,假定t企图在一次对 PipedOutputStream的write()方法的调用中向对应的PipedOutputStream写入2000字节的数据。在t线程阻塞以前,它最多可以写入1024字节的数据(PipedInputStream内部缓冲区的大小)。然而,一旦t被阻塞,读取 PipedInputStream的操做就不再会出现,由于t是惟一读取PipedInputStream的线程。这样,t线程已经彻底被阻塞,同时,全部其余试图向PipedOutputStream写入数据的线程也将遇到一样的情形。

这并不意味着在一次write()调用中不能写入多于1024字节的数据。但应当保证,在写入数据的同时,有另外一个线程从PipedInputStream读取数据。

Listing 2示范了这个问题。这个程序用一个线程交替地读取PipedInputStream和写入PipedOutputStream。每次调用write()向 PipedInputStream的缓冲区写入20字节,每次调用read()只从缓冲区读取并删除10个字节。内部缓冲区最终会被写满,致使写操做阻塞。因为咱们用同一个线程执行读、写操做,一旦写操做被阻塞,就不能再从PipedInputStream读取数据。

【Listing 2:用同一个线程执行读/写操做致使线程阻塞】

import java.io.*;
public class Listing2 {
    static PipedInputStream pipedIS = new PipedInputStream();
    static PipedOutputStream pipedOS = 
        new PipedOutputStream();
    public static void main(String[] a){
        try {
            pipedIS.connect(pipedOS);
        }
        catch(IOException e) {
            System.err.println("链接失败");
                System.exit(1);
            }
        byte[] inArray = new byte[10];
        byte[] outArray = new byte[20];
        int bytesRead = 0;
        try {
            // 向pipedOS发送20字节数据

            pipedOS.write(outArray, 0, 20);
            System.out.println(" 已发送20字节...");
           // 在每一次循环迭代中,读入10字节

           // 发送20字节

            bytesRead = pipedIS.read(inArray, 0, 10);
            int i=0;
            while(bytesRead != -1) {
                pipedOS.write(outArray, 0, 20);
                System.out.println(" 已发送20字节..."+i);
                i++;
                bytesRead = pipedIS.read(inArray, 0, 10);
            }
        }
        catch(IOException e) {
                System.err.println("读取pipedIS时出现错误: " + e);
                System.exit(1);
        }
    } // main()

}
只要把读/写操做分开到不一样的线程,Listing 2的问题就能够轻松地解决。Listing 3是Listing 2通过修改后的版本,它在一个单独的线程中执行写入PipedOutputStream的操做(和读取线程不一样的线程)。为证实一次写入的数据能够超过 1024字节,咱们让写操做线程每次调用PipedOutputStream的write()方法时写入2000字节。那么,在 startWriterThread()方法中建立的线程是否会阻塞呢?按照Java运行时线程调度机制,它固然会阻塞。写操做在阻塞以前实际上最多只能写入1024字节的有效载荷(即PipedInputStream缓冲区的大小)。但这并不会成为问题,由于主线程(main)很快就会从 PipedInputStream的循环缓冲区读取数据,空出缓冲区空间。最终,写操做线程会从上一次停止的地方从新开始,写入2000字节有效载荷中的剩余部分。

【Listing 3:把读/写操做分开到不一样的线程】

import java.io.*;
public class Listing3 {
    static PipedInputStream pipedIS =
        new PipedInputStream();
    static PipedOutputStream pipedOS =
        new PipedOutputStream();
    public static void main(String[] args) {
        try {
            pipedIS.connect(pipedOS);
        }
        catch(IOException e) {
            System.err.println("链接失败");
            System.exit(1);
        }
        byte[] inArray = new byte[10];
        int bytesRead = 0;
        // 启动写操做线程

        startWriterThread();
        try {
            bytesRead = pipedIS.read(inArray, 0, 10);
            while(bytesRead != -1) {
                System.out.println("已经读取" +
                    bytesRead + "字节...");
                bytesRead = pipedIS.read(inArray, 0, 10);
            }
        }
        catch(IOException e) {
            System.err.println("读取输入错误.");
            System.exit(1);
        }
    } // main()

    // 建立一个独立的线程

    // 执行写入PipedOutputStream的操做

    private static void startWriterThread() {
        ne工具

相关文章
相关标签/搜索