PipedWriter和PipedReader源码分析_动力节点Java学院整理
PipedWriter和PipedReader源码分析
1.PipedWriter源码(基于jdk1.7.40)
packagejava.io; publicclassPipedWriterextendsWriter{ //与PipedWriter通信的PipedReader对象 privatePipedReadersink; //PipedWriter的关闭标记 privatebooleanclosed=false; //构造函数,指定配对的PipedReader publicPipedWriter(PipedReadersnk)throwsIOException{ connect(snk); } //构造函数 publicPipedWriter(){ } //将“PipedWriter”和“PipedReader”连接。 publicsynchronizedvoidconnect(PipedReadersnk)throwsIOException{ if(snk==null){ thrownewNullPointerException(); }elseif(sink!=null||snk.connected){ thrownewIOException("Alreadyconnected"); }elseif(snk.closedByReader||closed){ thrownewIOException("Pipeclosed"); } sink=snk; snk.in=-1; snk.out=0; //设置“PipedReader”和“PipedWriter”为已连接状态 //connected是PipedReader中定义的,用于表示“PipedReader和PipedWriter”是否已经连接 snk.connected=true; } //将一个字符c写入“PipedWriter”中。 //将c写入“PipedWriter”之后,它会将c传输给“PipedReader” publicvoidwrite(intc)throwsIOException{ if(sink==null){ thrownewIOException("Pipenotconnected"); } sink.receive(c); } //将字符数组b写入“PipedWriter”中。 //将数组b写入“PipedWriter”之后,它会将其传输给“PipedReader” publicvoidwrite(charcbuf[],intoff,intlen)throwsIOException{ if(sink==null){ thrownewIOException("Pipenotconnected"); }elseif((off|len|(off+len)|(cbuf.length-(off+len)))<){ thrownewIndexOutOfBoundsException(); } sink.receive(cbuf,off,len); } //清空“PipedWriter”。 //这里会调用“PipedReader”的notifyAll(); //目的是让“PipedReader”放弃对当前资源的占有,让其它的等待线程(等待读取PipedWriter的线程)读取“PipedWriter”的值。 publicsynchronizedvoidflush()throwsIOException{ if(sink!=null){ if(sink.closedByReader||closed){ thrownewIOException("Pipeclosed"); } synchronized(sink){ sink.notifyAll(); } } } //关闭“PipedWriter”。 //关闭之后,会调用receivedLast()通知“PipedReader”它已经关闭。 publicvoidclose()throwsIOException{ closed=true; if(sink!=null){ sink.receivedLast(); } } }
2.PipedReader源码(基于jdk1.7.40)
packagejava.io; publicclassPipedReaderextendsReader{ //“PipedWriter”是否关闭的标记 booleanclosedByWriter=false; //“PipedReader”是否关闭的标记 booleanclosedByReader=false; //“PipedReader”与“PipedWriter”是否连接的标记 //它在PipedWriter的connect()连接函数中被设置为true booleanconnected=false; ThreadreadSide;//读取“管道”数据的线程 ThreadwriteSide;//向“管道”写入数据的线程 //“管道”的默认大小 privatestaticfinalintDEFAULT_PIPE_SIZE=1024; //缓冲区 charbuffer[]; //下一个写入字符的位置。in==out代表满,说明“写入的数据”全部被读取了。 intin=-; //下一个读取字符的位置。in==out代表满,说明“写入的数据”全部被读取了。 intout=; //构造函数:指定与“PipedReader”关联的“PipedWriter” publicPipedReader(PipedWritersrc)throwsIOException{ this(src,DEFAULT_PIPE_SIZE); } //构造函数:指定与“PipedReader”关联的“PipedWriter”,以及“缓冲区大小” publicPipedReader(PipedWritersrc,intpipeSize)throwsIOException{ initPipe(pipeSize); connect(src); } //构造函数:默认缓冲区大小是1024字符 publicPipedReader(){ initPipe(DEFAULT_PIPE_SIZE); } //构造函数:指定缓冲区大小是pipeSize publicPipedReader(intpipeSize){ initPipe(pipeSize); } //初始化“管道”:新建缓冲区大小 privatevoidinitPipe(intpipeSize){ if(pipeSize<=0){ thrownewIllegalArgumentException("Pipesize<=0"); } buffer=newchar[pipeSize]; } //将“PipedReader”和“PipedWriter”绑定。 //实际上,这里调用的是PipedWriter的connect()函数 publicvoidconnect(PipedWritersrc)throwsIOException{ src.connect(this); } //接收int类型的数据b。 //它只会在PipedWriter的write(intb)中会被调用 synchronizedvoidreceive(intc)throwsIOException{ //检查管道状态 if(!connected){ thrownewIOException("Pipenotconnected"); }elseif(closedByWriter||closedByReader){ thrownewIOException("Pipeclosed"); }elseif(readSide!=null&&!readSide.isAlive()){ thrownewIOException("Readenddead"); } //获取“写入管道”的线程 writeSide=Thread.currentThread(); //如果“管道中被读取的数据,等于写入管道的数据”时, //则每隔1000ms检查“管道状态”,并唤醒管道操作:若有“读取管道数据线程被阻塞”,则唤醒该线程。 while(in==out){ if((readSide!=null)&&!readSide.isAlive()){ thrownewIOException("Pipebroken"); } /*full:kickanywaitingreaders*/ notifyAll(); try{ wait(1000); }catch(InterruptedExceptionex){ thrownewjava.io.InterruptedIOException(); } } if(in<0){ in=0; out=0; } buffer[in++]=(char)c; if(in>=buffer.length){ in=0; } } //接收字符数组b。 synchronizedvoidreceive(charc[],intoff,intlen)throwsIOException{ while(--len>=){ receive(c[off++]); } } //当PipedWriter被关闭时,被调用 synchronizedvoidreceivedLast(){ closedByWriter=true; notifyAll(); } //从管道(的缓冲)中读取一个字符,并将其转换成int类型 publicsynchronizedintread()throwsIOException{ if(!connected){ thrownewIOException("Pipenotconnected"); }elseif(closedByReader){ thrownewIOException("Pipeclosed"); }elseif(writeSide!=null&&!writeSide.isAlive() &&!closedByWriter&&(in<)){ thrownewIOException("Writeenddead"); } readSide=Thread.currentThread(); inttrials=2; while(in<0){ if(closedByWriter){ /*closedbywriter,returnEOF*/ return-1; } if((writeSide!=null)&&(!writeSide.isAlive())&&(--trials<)){ thrownewIOException("Pipebroken"); } /*mightbeawriterwaiting*/ notifyAll(); try{ wait(1000); }catch(InterruptedExceptionex){ thrownewjava.io.InterruptedIOException(); } } intret=buffer[out++]; if(out>=buffer.length){ out=0; } if(in==out){ /*nowempty*/ in=-1; } returnret; } //从管道(的缓冲)中读取数据,并将其存入到数组b中 publicsynchronizedintread(charcbuf[],intoff,intlen)throwsIOException{ if(!connected){ thrownewIOException("Pipenotconnected"); }elseif(closedByReader){ thrownewIOException("Pipeclosed"); }elseif(writeSide!=null&&!writeSide.isAlive() &&!closedByWriter&&(in<0)){ thrownewIOException("Writeenddead"); } if((off<0)||(off>cbuf.length)||(len<0)|| ((off+len)>cbuf.length)||((off+len)<0)){ thrownewIndexOutOfBoundsException(); }elseif(len==0){ return0; } /*possiblywaitonthefirstcharacter*/ intc=read(); if(c<0){ return-1; } cbuf[off]=(char)c; intrlen=1; while((in>=0)&&(--len>0)){ cbuf[off+rlen]=buffer[out++]; rlen++; if(out>=buffer.length){ out=0; } if(in==out){ /*nowempty*/ in=-; } } returnrlen; } //是否能从管道中读取下一个数据 publicsynchronizedbooleanready()throwsIOException{ if(!connected){ thrownewIOException("Pipenotconnected"); }elseif(closedByReader){ thrownewIOException("Pipeclosed"); }elseif(writeSide!=null&&!writeSide.isAlive() &&!closedByWriter&&(in<)){ thrownewIOException("Writeenddead"); } if(in<0){ returnfalse; }else{ returntrue; } } //关闭PipedReader publicvoidclose()throwsIOException{ in=-; closedByReader=true; } }
示例
下面,我们看看多线程中通过PipedWriter和PipedReader通信的例子。例子中包括3个类:Receiver.java,Sender.java和PipeTest.java
Receiver.java的代码如下:
importjava.io.IOException; importjava.io.PipedReader; @SuppressWarnings("all") /** *接收者线程 */ publicclassReceiverextendsThread{ //管道输入流对象。 //它和“管道输出流(PipedWriter)”对象绑定, //从而可以接收“管道输出流”的数据,再让用户读取。 privatePipedReaderin=newPipedReader(); //获得“管道输入流对象” publicPipedReadergetReader(){ returnin; } @Override publicvoidrun(){ readMessageOnce(); //readMessageContinued(); } //从“管道输入流”中读取次数据 publicvoidreadMessageOnce(){ //虽然buf的大小是2048个字符,但最多只会从“管道输入流”中读取1024个字符。 //因为,“管道输入流”的缓冲区大小默认只有1024个字符。 char[]buf=newchar[2048]; try{ intlen=in.read(buf); System.out.println(newString(buf,0,len)); in.close(); }catch(IOExceptione){ e.printStackTrace(); } } //从“管道输入流”读取>1024个字符时,就停止读取 publicvoidreadMessageContinued(){ inttotal=0; while(true){ char[]buf=newchar[]; try{ intlen=in.read(buf); total+=len; System.out.println(newString(buf,,len)); //若读取的字符总数>1024,则退出循环。 if(total>1024) break; }catch(IOExceptione){ e.printStackTrace(); } } try{ in.close(); }catch(IOExceptione){ e.printStackTrace(); } } } Sender.java的代码如下: importjava.io.IOException; importjava.io.PipedWriter; @SuppressWarnings("all") /** *发送者线程 */ publicclassSenderextendsThread{ //管道输出流对象。 //它和“管道输入流(PipedReader)”对象绑定, //从而可以将数据发送给“管道输入流”的数据,然后用户可以从“管道输入流”读取数据。 privatePipedWriterout=newPipedWriter(); //获得“管道输出流”对象 publicPipedWritergetWriter(){ returnout; } @Override publicvoidrun(){ writeShortMessage(); //writeLongMessage(); } //向“管道输出流”中写入一则较简短的消息:"thisisashortmessage" privatevoidwriteShortMessage(){ StringstrInfo="thisisashortmessage"; try{ out.write(strInfo.toCharArray()); out.close(); }catch(IOExceptione){ e.printStackTrace(); } } //向“管道输出流”中写入一则较长的消息 privatevoidwriteLongMessage(){ StringBuildersb=newStringBuilder(); //通过for循环写入1020个字符 for(inti=0;i<102;i++) sb.append("0123456789"); //再写入26个字符。 sb.append("abcdefghijklmnopqrstuvwxyz"); //str的总长度是1020+26=1046个字符 Stringstr=sb.toString(); try{ //将1046个字符写入到“管道输出流”中 out.write(str); out.close(); }catch(IOExceptione){ e.printStackTrace(); } } }
PipeTest.java的代码如下:
importjava.io.PipedReader; importjava.io.PipedWriter; importjava.io.IOException; @SuppressWarnings("all") /** *管道输入流和管道输出流的交互程序 */ publicclassPipeTest{ publicstaticvoidmain(String[]args){ Sendert1=newSender(); Receivert2=newReceiver(); PipedWriterout=t1.getWriter(); PipedReaderin=t2.getReader(); try{ //管道连接。下面句话的本质是一样。 //out.connect(in); in.connect(out); /** *Thread类的START方法: *使该线程开始执行;Java虚拟机调用该线程的run方法。 *结果是两个线程并发地运行;当前线程(从调用返回给start方法)和另一个线程(执行其run方法)。 *多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。 */ t.start(); t.start(); }catch(IOExceptione){ e.printStackTrace(); } } }
运行结果:
thisisashortmessage
结果说明:
(01)in.connect(out);
它的作用是将“管道输入流”和“管道输出流”关联起来。查看PipedWriter.java和PipedReader.java中connect()的源码;我们知道out.connect(in);等价于in.connect(out);
(02)
t1.start();//启动“Sender”线程
t2.start();//启动“Receiver”线程
先查看Sender.java的源码,线程启动后执行run()函数;在Sender.java的run()中,调用writeShortMessage();
writeShortMessage();的作用就是向“管道输出流”中写入数据"thisisashortmessage";这条数据会被“管道输入流”接收到。下面看看这是如何实现的。
先看write(charchar的源码。PipedWriter.java继承于Writer.java;Writer.java中write(charc[])的源码如下:
publicvoidwrite(charcbuf[])throwsIOException{ write(cbuf,0,cbuf.length); }
实际上write(charc[])是调用的PipedWriter.java中的write(charc[],intoff,intlen)函数。查看write(charc[],intoff,intlen)的源码,我们发现:它会调用sink.receive(cbuf,off,len);进一步查看receive(charc[],intoff,intlen)的定义,我们知道sink.receive(cbuf,off,len)的作用就是:将“管道输出流”中的数据保存到“管道输入流”的缓冲中。而“管道输入流”的缓冲区buffer的默认大小是1024个字符。
至此,我们知道:t1.start()启动Sender线程,而Sender线程会将数据"thisisashortmessage"写入到“管道输出流”;而“管道输出流”又会将该数据传输给“管道输入流”,即而保存在“管道输入流”的缓冲中。
接下来,我们看看“用户如何从‘管道输入流'的缓冲中读取数据”。这实际上就是Receiver线程的动作。
t2.start()会启动Receiver线程,从而执行Receiver.java的run()函数。查看Receiver.java的源码,我们知道run()调用了readMessageOnce()。
而readMessageOnce()就是调用in.read(buf)从“管道输入流in”中读取数据,并保存到buf中。
通过上面的分析,我们已经知道“管道输入流in”的缓冲中的数据是"thisisashortmessage";因此,buf的数据就是"thisisashortmessage"。
为了加深对管道的理解。我们接着进行下面两个小试验。
试验一:修改Sender.java
将
publicvoidrun(){ writeShortMessage(); //writeLongMessage(); }
修改为
publicvoidrun(){ //writeShortMessage(); writeLongMessage(); }
运行程序。运行结果如下:
从中,我们看出,程序运行出错!抛出异常java.io.IOException:Pipeclosed
为什么会这样呢?
我分析一下程序流程。
(01)在PipeTest中,通过in.connect(out)将输入和输出管道连接起来;然后,启动两个线程。t1.start()启动了线程Sender,t2.start()启动了线程Receiver。
(02)Sender线程启动后,通过writeLongMessage()写入数据到“输出管道”,out.write(str.toCharArray())共写入了1046个字符。而根据PipedWriter的源码,PipedWriter的write()函数会调用PipedReader的receive()函数。而观察PipedReader的receive()函数,我们知道,PipedReader会将接受的数据存储缓冲区。仔细观察receive()函数,有如下代码:
while(in==out){ if((readSide!=null)&&!readSide.isAlive()){ thrownewIOException("Pipebroken"); } /*full:kickanywaitingreaders*/ notifyAll(); try{ wait(1000); }catch(InterruptedExceptionex){ thrownewjava.io.InterruptedIOException(); } }
而in和out的初始值分别是in=-1,out=0;结合上面的while(in==out)。我们知道,它的含义就是,每往管道中写入一个字符,就达到了in==out这个条件。然后,就调用notifyAll(),唤醒“读取管道的线程”。
也就是,每往管道中写入一个字符,都会阻塞式的等待其它线程读取。
然而,PipedReader的缓冲区的默认大小是1024!但是,此时要写入的数据却有1046!所以,一次性最多只能写入1024个字符。
(03)Receiver线程启动后,会调用readMessageOnce()读取管道输入流。读取1024个字符会,会调用close()关闭,管道。
由(02)和(03)的分析可知,Sender要往管道写入1046个字符。其中,前1024个字符(缓冲区容量是1024)能正常写入,并且每写入一个就读取一个。当写入1025个字符时,依然是依次的调用PipedWriter.java中的write();然后,write()中调用PipedReader.java中的receive();在PipedReader.java中,最终又会调用到receive(intc)函数。而此时,管道输入流已经被关闭,也就是closedByReader为true,所以抛出thrownewIOException("Pipeclosed")。
我们对“试验一”继续进行修改,解决该问题。
试验二:在“试验一”的基础上继续修改Receiver.java
将
publicvoidrun(){ readMessageOnce(); //readMessageContinued(); }
修改为
publicvoidrun(){ //readMessageOnce(); readMessageContinued(); }
此时,程序能正常运行。运行结果为:
01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
012345678901234567890123456789abcd
efghijklmnopqrstuvwxyz
以上所述是小编给大家介绍的PipedWriter和PipedReader源码分析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!