Java IO流之PipedWriter和PipedReader分析

简介

PipedWriter和PipedReader分别是字符管道输出流和字符管道输入流,是字符流中"管道流",可以实现同一个进程中两个线程之间的通信,与PipedOutputStream和PipedInputStream相比,功能类似.区别是前者传输的字符数据,而后者传输的是字节数据.

不同线程之间通信的流程大致是:PipedWriter与PipedReader流进行连接,写入线程通过将字符管道输出流写入数据,实际将数据写到了与PipedWriter连接的PipedReader流中的缓冲区,然后读取线程通过PipedReader流读取之前存储在缓冲区里面的字符数据.

PipedWriter介绍

1.构造方法

public PipedWriter(PipedReader snk){}
public PipedWriter() {}
  • 有参构造方法,参数传入字符管道输入流,创建的是与指定字符管道输入流连接的字符管道输出流.
  • 无参构造方法,创建的是没有任何连接的字符管道输出流.

2.内部变量

private PipedReader sink;
private boolean closed = false;
  • 与字符管道输出流连接的字符管道输入流.
  • 用于PipedWriter流是否关闭的标识.用于检查连接的状态.

3.内部方法

public synchronized void connect(PipedReader snk){}
public void write(int c){}
public void write(char cbuf[], int off, int len){}
public synchronized void flush(){}
public void close(){}
  • connect(PipedReader snk)---字符管道输入流和字符管道输出流连接.
  • write(int c)---将一个字符写入缓冲区中
  • write(char cbuf[],int off,int len)---将字符数组cbuf中off位置开始,最多len个字符写到缓冲区.
  • flush()---刷新缓冲区
  • close()---关闭字符管道输出流,释放相关资源.

PipedReader介绍

1.构造方法

public PipedReader(PipedWriter src) {}
public PipedReader(PipedWriter src, int pipeSize) {}
public PipedReader() {}
public PipedReader(int pipeSize){} 
  • PipedReader(PipedWriter src)---创建与字符管道输出流连接的字符管道输入流.缓冲区大小为默认的1024个字符.
  • PipedReader(PipedWriter src,int pipeSize)---创建指定缓冲区大小pipeSize,并与字符管道输出流连接的字符管道输入流.
  • PipedReader()---无参构造方法,创建了未连接的字符管道输入流,缓冲区大小为默认的1024个字符,
  • PipedReader(int pipeSize)---创建了未连接的字符管道输入流,缓冲区大小为指定的pipeSize.

2.内部变量

boolean closedByWriter = false;
boolean closedByReader = false;
boolean connected = false;
Thread readSide;
Thread writeSide;
char buffer[];
int in = -1;
int out = 0;
  • closeByWriter---标识的是PipedWriter流是否关闭.
  • closeByReader---标识的是PipedReader流是否关闭.
  • connected---字符管道输入流和字符管道输出流是否连接.
  • readSide---读取缓冲区数据的线程.
  • writeSide---写入缓冲区数据的线程
  • buffer---字符管道输入流中缓冲区,即为字符数组
  • in---缓冲区的索引值,表示的是从PipedWriter接收到的字符数据存储到缓冲数组buffer中位置索引.
  • out---缓冲区索引值,表示的是管道输入流中读取下一个字节的位置.

3.内部方法

public void connect(PipedWriter src){}
synchronized void receive(int c){}
synchronized void receive(char c[], int off, int len){}
synchronized void receivedLast() {}
public synchronized int read() {}
public synchronized int read(char cbuf[], int off, int len){}
public synchronized boolean ready(){}
public void close() {}
  • connect(PipedWriter src)---字符管道输入流和字符管道输出流连接.
  • receive(int c)---从字符管道输出流中接收单个字符数据,存储到字符管道输入流的缓冲区.
  • receive(char c[],int off,int len)---从字符管道输出流中接收字符数组数据,存储到字符管道输入流的缓冲区.
  • receivedLast()---通知所有等待线程,缓冲区所有的字符已被读取.
  • read()---从缓冲区中读取一个字符数据.
  • read(char cbuf[],int off, int len)---从缓冲区中读取len个字符数据到字符数组cbuf中,位置从off开始.
  • ready()---字符管道输入流中缓冲区数据是否可读取.如不为空可读取,返回true.
  • close()----关闭流,释放关联的资源.

案例

public class PipedWriteDemo {
  public static void main(String[] args) throws IOException {
    Sender sender = new Sender();
    Receiver receiver = new Receiver();
    PipedWriter pipedWriter= sender.getPipedWriter();
    PipedReader pipedReader = receiver.getPipedReader();
    pipedReader.connect(pipedWriter);
    sender.start();
    receiver.start();
  }
}
public class Sender extends Thread {

  private PipedWriter pipedWriter = new PipedWriter();
  
  public PipedWriter getPipedWriter() {
    return pipedWriter;
  }

  public void run() {
    try {
      pipedWriter.write("this is a demo about PipedWriter and PipedReader".toCharArray());
      pipedWriter.append("0123");
      pipedWriter.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}
public class Receiver extends Thread{
  
  private PipedReader pipedReader = new PipedReader();

  public PipedReader getPipedReader() {
    return pipedReader;
  }

  public void run() {
    try {
     char[] buf = new char[1024];
     pipedReader.read(buf);
     System.out.println(new String(buf));
     pipedReader.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

运行结果:

this is a demo about PipedWriter and PipedReader0123

源码分析

1.PipedWriter源码分析

public class PipedWriter extends Writer {

    //与PipedWriter流连接的PipedReader
    private PipedReader sink;

    //PipedWriter流是否关闭的标识,主要是用于检查连接状态
    private boolean closed = false;

    //有参构造,创建一个与管道输入流连接的管道输出流
    public PipedWriter(PipedReader snk)  throws IOException {
        connect(snk);
    }

    //无参构造,不会与管道输入流连接
    public PipedWriter() {
    }

    //字符管道输出流与字符管道输入流连接
    public synchronized void connect(PipedReader snk) throws IOException {
        if (snk == null) {
            throw new NullPointerException();
        } else if (sink != null || snk.connected) {
            throw new IOException("Already connected");
        } else if (snk.closedByReader || closed) {
            throw new IOException("Pipe closed");
        }

        sink = snk;
        snk.in = -1;
        snk.out = 0;
        snk.connected = true;
    }

    //将一个字符数据写到字符管道输出流中
    public void write(int c)  throws IOException {
        if (sink == null) {
            throw new IOException("Pipe not connected");
        }
        sink.receive(c);
    }

    //将字符数组cbuf中off位置开始,len个字节写到字符管道输出流中
    //当读取线程关闭情况,会抛出异常
    public void write(char cbuf[], int off, int len) throws IOException {
        if (sink == null) {
            throw new IOException("Pipe not connected");
        } else if ((off | len | (off + len) | (cbuf.length - (off + len))) < 0) {
            throw new IndexOutOfBoundsException();
        }
        sink.receive(cbuf, off, len);
    }

     //刷新管道输出流,强制将缓冲字符写出.
    public synchronized void flush() throws IOException {
        if (sink != null) {
            if (sink.closedByReader || closed) {
                throw new IOException("Pipe closed");
            }
            //"通知"读取线程读取
            synchronized (sink) {
                sink.notifyAll();
            }
        }
    }

    //关闭字符管道输出流,释放关联的资源
    public void close()  throws IOException {
        closed = true;
        if (sink != null) {
            sink.receivedLast();
        }
    }
}

2.PipedReader源码分析

public class PipedReader extends Reader {
  
    boolean closedByWriter = false;//标识pipedWriter是否关闭
    boolean closedByReader = false;//标识PipedReader是否关闭
    boolean connected = false;//PipedWriter与PipedReader连接是否关闭

    //读取缓冲区数据的线程
    Thread readSide;
    //向缓冲区写入数据的线程
    Thread writeSide;


    //默认缓冲区大小
    private static final int DEFAULT_PIPE_SIZE = 1024;

    //字符管道输入流中缓冲区,即字符数组buffer
    char buffer[];

    //注意的是in==out的时候,表明缓冲区已满
    //接受从PipedWriter传过来的字符数据存储到缓冲数组buffer的索引位置,in=-1表示缓冲区是空的.
    int in = -1;

    //从pipedReader缓冲区中读取下一个字符的索引
    int out = 0;

    //创建一个管道输出流连接的,缓冲区大小为默认的1024个字符的PipedReader
    public PipedReader(PipedWriter src) throws IOException {
        this(src, DEFAULT_PIPE_SIZE);
    }

    //创建一个与管道输出流连接的,并且指定了缓冲区大小为pipeSize的PipedReader
    public PipedReader(PipedWriter src, int pipeSize) throws IOException {
        initPipe(pipeSize);
        connect(src);
    }

    //无参构造方法,创建默认大小为1024个字符的缓冲区,与PipedWriter未连接
    public PipedReader() {
        initPipe(DEFAULT_PIPE_SIZE);
    }

    //有参构造方法,创建指定大小pipeSize的缓冲区,与PipedWriter未连接
    public PipedReader(int pipeSize) {
        initPipe(pipeSize);
    }

    private void initPipe(int pipeSize) {
        if (pipeSize <= 0) {
            throw new IllegalArgumentException("Pipe size <= 0");
        }
        buffer = new char[pipeSize];
    }

    //管道输入流和管道输出流连接
    public void connect(PipedWriter src) throws IOException {
        src.connect(this);
    }

    //从管道输出流中接收一个字符存储到管道输入流的缓冲区中
    synchronized void receive(int c) throws IOException {
        if (!connected) {
            throw new IOException("Pipe not connected");
        } else if (closedByWriter || closedByReader) {
            throw new IOException("Pipe closed");
        } else if (readSide != null && !readSide.isAlive()) {
            throw new IOException("Read end dead");
        }

        writeSide = Thread.currentThread();
        while (in == out) {
            if ((readSide != null) && !readSide.isAlive()) {
                throw new IOException("Pipe broken");
            }
            /* full: kick any waiting readers */
            notifyAll();
            try {
                wait(1000);
            } catch (InterruptedException ex) {
                throw new java.io.InterruptedIOException();
            }
        }
        if (in < 0) {
            in = 0;
            out = 0;
        }
        buffer[in++] = (char) c;
        if (in >= buffer.length) {
            in = 0;
        }
    }

    //从管道输出流中接收字符数组c,将off开始,len个字符存储到管道输入流的缓冲区
    synchronized void receive(char c[], int off, int len)  throws IOException {
        while (--len >= 0) {
            receive(c[off++]);
        }
    }

    //"通知"所有等待线程,所有字符已被读取
    synchronized void receivedLast() {
        closedByWriter = true;
        notifyAll();
    }

    //从管道输入流中读取一个字符,如果没有字符可读,将会返回-1.
    //此方法会一直阻塞直到有字符写到缓冲区中.
    public synchronized int read()  throws IOException {
        if (!connected) {
            throw new IOException("Pipe not connected");
        } else if (closedByReader) {
            throw new IOException("Pipe closed");
        } else if (writeSide != null && !writeSide.isAlive()
                   && !closedByWriter && (in < 0)) {
            throw new IOException("Write end dead");
        }

        readSide = Thread.currentThread();
        int trials = 2;
        while (in < 0) {
            if (closedByWriter) {
                /* closed by writer, return EOF */
                return -1;
            }
            if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0)) {
                throw new IOException("Pipe broken");
            }
            /* might be a writer waiting */
            //唤醒写入线程写入数据
            notifyAll();
            try {
                wait(1000);
            } catch (InterruptedException ex) {
                throw new java.io.InterruptedIOException();
            }
        }
        int ret = buffer[out++];
        if (out >= buffer.length) {
            out = 0;
        }
        //缓冲区所有的字符数据全被读取
        if (in == out) {
            /* now empty */
            in = -1;
        }
        return ret;
    }

    //从管道输入流中读取最多len个字符到字符数组cbuf中的off位置开始
    //如果存在剩余可读字符少于len情况,则实际读取的字符少于len个.
    //缓冲区没有数据情况下,此方法会一直阻塞
    public synchronized int read(char cbuf[], int off, int len)  throws IOException {
        if (!connected) {
            throw new IOException("Pipe not connected");
        } else if (closedByReader) {
            throw new IOException("Pipe closed");
        } else if (writeSide != null && !writeSide.isAlive()
                   && !closedByWriter && (in < 0)) {
            throw new IOException("Write end dead");
        }

        if ((off < 0) || (off > cbuf.length) || (len < 0) ||
            ((off + len) > cbuf.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

       //尝试先读取一个字符
        int c = read();
        if (c < 0) {
            return -1;
        }
        //先读取的单个字符放在cbuf数组的off开始位置
        cbuf[off] =  (char)c;
        int rlen = 1;
        //已读取一个字符c,所以要减掉一个字符--len
        while ((in >= 0) && (--len > 0)) {
            cbuf[off + rlen] = buffer[out++];
            rlen++;
            if (out >= buffer.length) {
                out = 0;
            }
            //in==out表示缓冲区数据已被读完
            if (in == out) {
                /* now empty */
                in = -1;
            }
        }
        return rlen;
    }

    //管道输入流中是否可读取,缓冲区不为空情况下,可读取,返回true
    public synchronized boolean ready() throws IOException {
        if (!connected) {
            throw new IOException("Pipe not connected");
        } else if (closedByReader) {
            throw new IOException("Pipe closed");
        } else if (writeSide != null && !writeSide.isAlive()
                   && !closedByWriter && (in < 0)) {
            throw new IOException("Write end dead");
        }
        if (in < 0) {
            return false;
        } else {
            return true;
        }
    }

    //关闭管道输入流,释放相关资源
    public void close()  throws IOException {
        in = -1;
        closedByReader = true;
    }
}

总结

1.in表示的是接收到从PipedWriter传输过来的数据存储到PipedReader的缓存区的索引位置,out表示的是从缓冲区读取到下一字符的索引.当in==out 的时候,表示是的是缓冲区的所有数据全部被读取.

1.PipedWriter与PipedReader实现进行线程之间通信,写入线程写到PipedWriter的字符数据,实际是存储到与PipedWriter连接的PipedReader中缓冲区里面,然后通过读取线程读取到缓冲区里面的数据.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值