Java IO - PipedReader& PipedWriter

本文深入探讨了PipedReader与PipedWriter的工作原理及使用方法,这两种流是Java中专门用于线程间通信的管道流。文章详细解析了它们的构造函数、连接方式以及读写操作的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基本概念

  • PipedReader& PipedWriter(管道字符输入流&管道输符字节流)是配套使用的。可以将管道输出流连接到管道输入流来创建通信管道。

  • 详细用法参见管道字节流

  • 继承关系:

这里写图片描述

这里写图片描述


源码探究

2.PipedReader

类结构图

Alt text

成员变量,同 PipedInputStream,这里不再分析

private static final int DEFAULT_PIPE_SIZE = 1024;
boolean closedByWriter = false;
boolean closedByReader = false;
boolean connected = false;
Thread readSide;
Thread writeSide;
char buffer[];
int in = -1;
int out = 0;

该类定义了4个构造函数,可分为两种,未连接管道输出流与连接管道输出流。观察代码,构造函数主要做了两件事:

  • 调用了 initPipe 方法来进行初始化,而该方法的主要作用是创建管道换区,如果未指定大小时,默认为 1024

  • 调用了 connect 方法来连接管道输出流,而该方法是在 PipedWriter 类中定义的,。它修改了输入流的成员变量值,令 in = -1; out = 0; connected = true;

public PipedReader(PipedWriter src) throws IOException {
    this(src, DEFAULT_PIPE_SIZE);
}

public PipedReader(PipedWriter src, int pipeSize) throws IOException {
    initPipe(pipeSize);
    connect(src);
}

public PipedReader() {
    initPipe(DEFAULT_PIPE_SIZE);
}

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);
}

下面来看 read 方法,这里定义了两种常见的读取数据的方式。

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) {
            return -1;
        }
        if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0)) {
            throw new IOException("Pipe broken");
        }
        //等待输出流输出数据
        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) {
        in = -1;
    }

    return ret;
}


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] = (char) c;

    int rlen = 1;

    //与 PipedInputStream 不同,本质也是按字符读取
    while ((in >= 0) && (--len > 0)) {

        cbuf[off + rlen] = buffer[out++];

        rlen++;

        if (out >= buffer.length) {
            out = 0;
        }

        if (in == out) {
            in = -1;
        }
    }

    return rlen;
}

重点来看 receive方法, 用于输入流接收信息,在输出流 write 操作时会被调用,我们也可以将其是为输出流的写入操作,只不过这里在输入流实现(因为是线程安全的方法,synchronized 生效的前提操作同一对象)。同样也有定义了两种常见的写入(接收)操作。

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");
        }

        //唤醒其他阻塞线程,当前线程进入阻塞
        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;
    }
}


synchronized void receive(char c[], int off, int len) throws IOException {
    while (--len >= 0) {
        //与管道输入流不同,这里调用 receive(int c) 来实现
        receive(c[off++]);
    }
}

synchronized void receivedLast() {
    closedByWriter = true;
    notifyAll();
}

最后来看看剩余的方法

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;
}
}

2.PipedWriter

类结构图

Alt text

成员变量

private PipedReader sink;
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;
}

接着来看 write 方法,定义了两种 write 方式,并且都通过输入流接收信息

public void write(int c) throws IOException {
    if (sink == null) {
        throw new IOException("Pipe not connected");
    }
    sink.receive(c);
}

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();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oxf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值