Java 日看一类(42)之IO包中的PipedInputStream

该类继承自InputStream

无引入包



该类的类头注释如下:

/**
 * A piped input stream should be connected
 * to a piped output stream; the piped  input
 * stream then provides whatever data bytes
 * are written to the piped output  stream.
 * Typically, data is read from a <code>PipedInputStream</code>
 * object by one thread  and data is written
 * to the corresponding <code>PipedOutputStream</code>
 * by some  other thread. Attempting to use
 * both objects from a single thread is not
 * recommended, as it may deadlock the thread.
 * The piped input stream contains a buffer,
 * decoupling read operations from write operations,
 * within limits.
 * A pipe is said to be <a name="BROKEN"> <i>broken</i> </a> if a
 * thread that was providing data bytes to the connected
 * piped output stream is no longer alive.
 *
 * @author  James Gosling
 * @see     java.io.PipedOutputStream
 * @since   JDK1.0
 */

大意如下:

一个管道输入流需要与管道输出流链接

管道输入流会获得管道出流被写入的任意byte数据

典型情况下,数据通过PipedInputStream被读取(单线程)并且数据被对应的PipedOutputSream写出

尝试通过单线程使用这两个对象是不推荐的

可能会造成线程的死锁

这个管道输入流包括了一个缓冲区,在缓冲区内可以分离读操作和写操作

如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏



该类含有如下的成员变量:

写关闭标志位

boolean closedByWriter = false;

读关闭标志位(要求每次都读取,敏感信息)

volatile boolean closedByReader = false;

连接状态

boolean connected = false;

读线程

Thread readSide;

写线程

Thread writeSide;

默认缓冲区大小

private static final int DEFAULT_PIPE_SIZE = 1024;

共用的管道缓冲区大小

protected static final int PIPE_SIZE = DEFAULT_PIPE_SIZE;

循环缓冲区

protected byte buffer[];

读取指针的偏移量

protected int in = -1;

写出指针的偏移量

protected int out = 0;



该类含有如下的成员方法:

构造函数(默认缓冲区大小

public PipedInputStream(PipedOutputStream src) throws IOException {
    this(src, DEFAULT_PIPE_SIZE);
}

构造函数(指定缓冲大小

public PipedInputStream(PipedOutputStream src, int pipeSize)
        throws IOException {
     initPipe(pipeSize);
     connect(src);
}

构造函数(暂时无链接,默认大小

public PipedInputStream() {
    initPipe(DEFAULT_PIPE_SIZE);
}

构造函数(同上,指定大小

public PipedInputStream(int pipeSize) {
    initPipe(pipeSize);
}

管道初始化函数(构造函数提取出的公因数

private void initPipe(int pipeSize) {
     if (pipeSize <= 0) {//检查缓冲区大小合理性
        throw new IllegalArgumentException("Pipe Size <= 0");
     }
     buffer = new byte[pipeSize];//初始化缓冲区
}

链接两个未被链接的管道(如果已链接就抛IOException

public void connect(PipedOutputStream src) throws IOException {
    src.connect(this);
}

接收一个byte数据(该方法会被阻塞,在没有有效输入时

protected synchronized void receive(int b) throws IOException {
    checkStateForReceive();//检查接收状态
    writeSide = Thread.currentThread();//获得当前的操作线程
    if (in == out)//如果写入指针和写出指针指向一个地方表示当前缓冲区满,无法接收
        awaitSpace();
    if (in < 0) {//如果输入指针小于0(为空),则初始化
        in = 0;
        out = 0;
    }
    buffer[in++] = (byte)(b & 0xFF);//将输入byte写入缓冲区
    if (in >= buffer.length) {//检查输出指针是否溢出,溢出则归0,可以使用in%=buffer.length,不过这样的操作效率就会变低
        in = 0;
    }
}

接收一个数组数据(也会被阻塞

synchronized void receive(byte b[], int off, int len)  throws IOException {
    checkStateForReceive();
    writeSide = Thread.currentThread();
    int bytesToTransfer = len;//获得需要写入的数据长度
    while (bytesToTransfer > 0) {//长度有效
        if (in == out)//满了
            awaitSpace();
        int nextTransferAmount = 0;//下次最多可写入的长度(因为可能存在写入长度超过缓冲区长度,所以分多次写入,每次写入都会覆盖上次的数据,需要在写入之前读取出上次的数据)
        if (out < in) {//in指针比较靠前
            nextTransferAmount = buffer.length - in;//获得剩余填充的数值长度
        } else if (in < out) {
            if (in == -1) {//缓冲区为空
                in = out = 0;
                nextTransferAmount = buffer.length - in;//获得缓冲区长度
            } else {
                nextTransferAmount = out - in;//获得可读取长度(如果读取到out就证明缓冲区已经读满了,再读入会影响数据
            }
        }
        if (nextTransferAmount > bytesToTransfer)//可一次写入
            nextTransferAmount = bytesToTransfer;
        assert(nextTransferAmount > 0);//断言大于0,在这里减少数值有效性检查,提高运行效率
        System.arraycopy(b, off, buffer, in, nextTransferAmount);
        bytesToTransfer -= nextTransferAmount;//获得剩余需转移数据
        off += nextTransferAmount;//修改需要读取的位置
        in += nextTransferAmount;
        if (in >= buffer.length) {//循环
            in = 0;
        }
    }
}

检查缓冲区状态

private void checkStateForReceive() 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");
    }
}

阻塞写入操作

private void awaitSpace() throws IOException {
    while (in == out) {
        checkStateForReceive();//检测有效性

        /* full: kick any waiting readers */
        notifyAll();//唤醒所有阻塞线程(实质只找一个,但是赋予所有线程争夺权
        try {
            wait(1000);//挂起线程(丢失锁的竞争权
        } catch (InterruptedException ex) {//抛IO中断
            throw new java.io.InterruptedIOException();
        }
    }
}

关闭写入,唤醒所有挂起的线程

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

向管道中写入数据

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)) {//写线程存在且并未活动且标志位小于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++] & 0xFF;//读出单个byte
    if (out >= buffer.length) {//循环
        out = 0;
    }
    if (in == out) {//读取完全部数据,置为空,方便操作
        /* now empty */
        in = -1;
    }

    return ret;//返回读取值
}

读取数组

public synchronized int read(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;
    }

    /* possibly wait on the first character */
    int c = read();//读取单个字节
    if (c < 0) {//判断文件是否读取完毕
        return -1;
    }
    b[off] = (byte) c;
    int rlen = 1;//实际读出的数据长度
    while ((in >= 0) && (len > 1)) {//缓冲区不为空且剩余需读取长度部不为0

        int available;//每次可以读出的有效长度

        if (in > out) {
            available = Math.min((buffer.length - out), (in - out));
        } else {
            available = buffer.length - out;
        }

        // A byte is read beforehand outside the loop
        if (available > (len - 1)) {//单次可读出的有效长度大于剩余需求长度
            available = len - 1;
        }
        System.arraycopy(buffer, out, b, off + rlen, available);
        out += available;
        rlen += available;
        len -= available;

        if (out >= buffer.length) {
            out = 0;
        }
        if (in == out) {
            /* now empty */
            in = -1;
        }
    }
    return rlen;//返回实际读取长度
}

返回可以不受阻塞地读取出的数值数

public synchronized int available() throws IOException {
    if(in < 0)
        return 0;
    else if(in == out)//当两个相同时则为满(因为如果输出操作将指针修改成相等状态时会将in修改为-1,标志为空)
        return buffer.length;
    else if (in > out)
        return in - out;
    else
        return in + buffer.length - out;
}

关闭管道

public void close()  throws IOException {
    closedByReader = true;//读取方关闭
    synchronized (this) {//输入流置空,清空缓冲内有效数据
        in = -1;
    }
}




该类实质是一个带有线程同步的循环数组,理解这个之后其实非常简单。该类主要用于进程间通信,需要注意的是一个管道必须要有两端才能成立,否则是不成立的。文中的写入写出输入输出可能会看的头晕,在阅读是可以思考一下,这个操作的主语是谁,是谁在操作,操作的又是谁,这些说法是从主语角度来写的。


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值