PipedOutputStream 및 PipedInputStream
Java에서는 PipedOutputStream 및 PipedInputStream은 각각 파이프 라인 출력 스트림과 파이프 라인 입력 스트림입니다.
그들의 기능은 멀티 스레드가 파이프 라인을 통해 스레드간에 통신 할 수 있도록하는 것입니다. 파이프 라인 통신을 사용하는 경우 PipedOutputStream 및 PipedInputStream을 서로 함께 사용해야합니다.
파이프 라인 통신을 사용할 때 일반적인 프로세스는 다음과 같습니다. 우리는 스레드 A의 PipedOutputStream에 데이터를 작성하고 이러한 데이터는 PipedOutputStream에 해당하는 PipedInputStream으로 자동 전송 된 다음 PipedInputStream의 버퍼에 저장됩니다. 현재 Thread B는 PipedInputStream에서 데이터를 읽습니다. 이것은 스레드 A와 스레드 B 사이의 의사 소통을 실현할 수 있습니다.
아래에서는 멀티 스레드의 파이프 라인을 통한 통신의 예를 살펴 봅니다. 예제에는 3 개의 클래스가 포함됩니다 : receiver.java, pipedstreamtest.java 및 sender.java.
수신자 코드는 다음과 같습니다.
import java.io.ioexception; import java.io.pipedInputStream; @SuppressWarnings ( "All") / *** 수신기 스레드* / public 클래스 수신기는 스레드 {// 파이프 라인 입력 스트림 객체를 확장합니다. // "PipedOutputStream"객체에 바인딩됩니다. // "PipedOutputStream"의 데이터를 수신 한 다음 사용자가 읽게 할 수 있습니다. 개인 PipedInputStream in = New PipedInputStream (); // "파이프 입력 스트림"객체 공개 pipedInputStream getInputStream () {return in; } @override public void run () {readMessageOnce (); // readMessAgeContinued (); } // "파이프 입력 스트림"에서 한 번 읽기 "public void void readMessageOnce () {// buf의 크기는 2048 바이트이지만"파이프 입력 스트림 "에서 최대 1024 바이트 만 읽습니다. // "파이프 입력 스트림"의 버퍼 크기는 기본적으로 1024 바이트에 불과하기 때문입니다. 바이트 [] buf = 새로운 바이트 [2048]; try {int len = in.read (buf); System.out.println (새 문자열 (buf, 0, len)); 넣다(); } catch (ioexception e) {e.printstacktrace (); }} // "파이프 입력 스트림"에서> 1024 바이트를 읽을 때 공개 void void readMessageContinued () {int total = 0; while (true) {byte [] buf = new Byte [1024]; try {int len = in.read (buf); 총 += 렌; System.out.println (새 문자열 (buf, 0, len)); // 읽은 총 바이트 수가> 1024 인 경우 루프를 종료하십시오. IF (총> 1024) 파손; } catch (ioexception e) {e.printstacktrace (); }} try {in.close (); } catch (ioexception e) {e.printstacktrace (); }}} Sender.java 코드는 다음과 같습니다.
import java.io.ioexception; import java.io.pipedoutputStream; @SuppressWarnings ( "All")/ *** Sender Thread*/ Public Class 발신자는 스레드 {// 파이프 라인 출력 스트림 객체를 확장합니다. // "pipedInputStream"객체에 바인딩됩니다. // 이것은 "pipedInputStream"의 데이터로 데이터를 전송할 수있게 한 다음 사용자는 "PipedInputStream"에서 데이터를 읽을 수 있습니다. Private PipedOutputStream out = New PipedOutputStream (); // "파이프 출력 스트림"객체 공개 pipedOutputStream getOutputStream () {return out; } @override public void run () {writesHortMessage (); // writeLongMessage (); } // "파이프 출력 스트림"에 짧은 메시지를 씁니다. try {out.write (strinfo.getBytes ()); out.close (); } catch (ioexception e) {e.printstacktrace (); }} // "파이프 출력 스트림"에 긴 메시지를 씁니다. private void writeLongMessage () {StringBuilder sb = new StringBuilder (); // (int i = 0; i <102; i ++) sb.append ( "0123456789"); // 26 바이트를 더 쓰십시오. sb.append ( "abcdefghijklmnopqrstuvwxyz"); // str의 총 길이는 1020+26 = 1046 바이트 스트링 str = sb.tostring (); {// 1046 바이트를 "파이프 출력 스트림"OUT.Write (str.getBytes ())에 쓰십시오. out.close (); } catch (ioexception e) {e.printstacktrace (); }}} PipedStreamTest.java 코드는 다음과 같습니다.
import java.io.pipedInputStream; import java.io.pipedoutputStream; import java.io.ioexception; @suppresswarnings ( "all") / *** 파이프 라인 입력 스트림 및 파이프 라인 출력 스트림을위한 대화식 프로그램* / public static void main (string) {sender t1 = newsender (); 수신기 t2 = 새로운 수신기 (); PipedOutputStream out = t1.getOutputStream (); pipedInputStream in = t2.getInputStream (); {// 파이프 연결을 시도하십시오. 다음 두 문장의 본질은 동일합니다. //out.connect(in); in.connect (out); /** * 스레드 클래스의 시작 방법 : * 스레드가 실행을 시작합니다. Java Virtual Machine은 Thread의 실행 메소드를 호출합니다. * 결과적으로 두 개의 스레드가 동시에 실행됩니다. 현재 스레드 (통화에서 시작 메소드로 반환 됨)와 다른 스레드 (실행 메소드 실행). * 스레드를 여러 번 시작하는 것은 불법입니다. 특히 스레드가 실행 된 경우 다시 시작할 수 없습니다. */ t1.start (); t2.start (); } catch (ioexception e) {e.printstacktrace (); }}} 실행 결과 :
이것은 짧은 메시지입니다
설명 :
(1) in.connect (out); "파이프 입력 스트림"및 "파이프 출력 스트림"을 연관시킵니다. PipedOutputStream.java 및 PipedInputStream.java에서 Connect ()의 소스 코드를 확인하십시오. 우리는 알고 있습니다. Connect (in); in.connect (out)와 동일합니다.
(2)
t1.start (); // "sender"스레드 t2.start ()를 시작합니다. // "수신기"스레드를 시작합니다
먼저 sender.java의 소스 코드를 확인하고 스레드가 시작된 후 run () 함수를 실행합니다. sender.java의 run ()에서 writesHortMessage ()를 호출합니다.
writesHortMessage ()의 함수; "파이프 출력 스트림"에 "이것은 짧은 메시지"데이터를 작성하는 것입니다. 이 데이터는 "파이프 입력 스트림"에 의해 수신됩니다. 이것이 어떻게 달성되는지 봅시다.
먼저 쓰기 소스 코드 (byte b [])를 살펴보고 outputStream.java에서 정의하겠습니다. PipedOutputStream.java는 outputStream.java에서 상속합니다. outputStream.java의 쓰기 소스 코드 (byte b [])는 다음과 같습니다.
public void write (byte b [])는 ioexception {쓰기 (b, 0, b.length);} 실제로, write (byte b [])는 pipedoutputstream.java에서 호출 쓰기 (byte b [], int off, int len) 함수입니다. 쓰기 소스 코드 (byte b [], int off, int len)를 살펴보면 Sink.Receive (B, Off, Len)를 호출 할 것임을 알았습니다. 수신 (byte b [], int off, int len)의 정의를 살펴보면 Sink.Receive (B, Off, Len)는 "파이프 출력 스트림"의 데이터를 "파이프 입력 스트림"의 버퍼에 저장하는 것임을 알고 있습니다. "파이프 입력 스트림"의 버퍼 버퍼의 기본 크기는 1024 바이트입니다.
이 시점에서 우리는 다음과 같이 알고 있습니다. t1.start ()는 발신자 스레드를 시작하고 발신자 스레드는 "파이프 출력 스트림"에 "짧은 메시지"데이터를 작성합니다. "파이프 출력 스트림"은 데이터를 "파이프 입력 스트림"으로 전송합니다. 즉, "파이프 입력 스트림"의 버퍼에 저장됩니다.
다음으로 "사용자가 '파이프 입력 스트림'의 버퍼에서 데이터를 읽는 방법"을 살펴 봅니다. 이것은 실제로 수신기 스레드의 동작입니다.
t2.start ()는 수신기 스레드를 시작하여 수신기.java run () 함수를 실행합니다. 수신자의 소스 코드를 살펴보면 run ()가 readMessageOnce ()를 호출한다는 것을 알고 있습니다.
readMessageOnce ()는 in.read (buf)를 호출하여 "파이프 입력 스트림"의 데이터를 읽고 BUF에 저장해야합니다.
위의 분석을 통해, 우리는 이미 "파이프 입력 스트림"의 버퍼에있는 데이터가 "짧은 메시지"라는 것을 이미 알고 있습니다. 따라서 BUF의 데이터는 "이것은 짧은 메시지입니다"입니다.
파이프 라인의 이해를 심화시키기 위해. 우리는 다음 두 가지 작은 실험을 계속할 것입니다.
실험 1 : sender.java를 수정합니다
할 것이다
public void run () {writesHortMessage (); // writeLongMessage ();} 수정
public void run () {// writesHortMessage (); writelongmessage ();} 프로그램을 실행하십시오. 실행 결과는 다음과 같습니다.
이 데이터는 WriteLongMessage ()를 통해 "파이프 출력 스트림"에 기록 된 다음 "파이프 입력 스트림"으로 전송 한 다음 "파이프 입력 스트림"의 버퍼에 저장됩니다. 그런 다음 사용자가 버퍼에서 읽으십시오.
그런 다음 writelongmessage ()의 소스 코드를 관찰하십시오. STR의 길이는 1046 바이트이며 실행 결과는 1024 바이트입니다! 왜 이런 일이 일어나고 있습니까?
그 이유는 간단합니다. 파이프 라인 입력 스트림의 버퍼의 기본 크기는 1024 바이트입니다. 따라서 최대 1024 바이트를 작성할 수 있습니다.
PipedInputStream.java의 소스 코드를 관찰하면 더 철저하게 이해할 수 있습니다.
개인 정적 최종 int default_pipe_size = 1024; public PipedInputStream () {initpipe (default_pipe_size);} 기본 생성자는 initpipe (default_pipe_size)를 호출하고 소스 코드는 다음과 같습니다.
private void initpipe (int pipesize) {if (pipesize <= 0) {새로운 불법 불법 행위 ( "파이프 크기 <= 0"); } buffer = new Byte [Pipesize];} 이것으로부터, 우리는 버퍼 버퍼의 기본 크기가 1024 바이트임을 알 수 있습니다.
실험 2 : "실험 1"을 기준으로 수신기를 계속 수정하십시오.
할 것이다
public void run () {readMessageOnce (); // readMessageContinued ();} 수정
public void run () {// readMessageOnce (); readmessagecontinued ();} 프로그램을 실행하십시오. 실행 결과는 다음과 같습니다.
이 결과는 "입력 버퍼"에 기록 된 전체 데이터입니다.
Pipedwriter 및 PipedReader
Pipedwriter는 캐릭터 파이프 라인 출력 스트림으로, 작가로부터 상속됩니다.
PipedReader는 Writer의 상속을받는 문자 파이프 라인 입력 스트림입니다.
PipedWriter 및 PipedReader의 기능은 파이프 라인을 통해 스레드간에 통신하는 것입니다. 파이프 라인 통신을 사용하는 경우 PipedWriter와 PipedReader는 서로 함께 사용해야합니다.
아래에서는 Multithreading에서 PipedWriter 및 PipedReader를 통한 커뮤니케이션의 예를 살펴 봅니다. 예제에는 3 개의 클래스가 포함됩니다 : receiver.java, sender.java 및 pipetest.java
수신자 코드는 다음과 같습니다.
import java.io.ioexception; import java.io.pipedReader; @SuppressWarnings ( "All") / *** 수신기 스레드* / public 클래스 수신기는 스레드 {// 파이프 라인 입력 스트림 객체를 확장합니다. // "PipedWriter"객체에 바인딩됩니다. // 이렇게하면 "PipedWriter"의 데이터를 수신 한 다음 사용자가 읽을 수 있습니다. 개인 PipedReader in = new PipedReader (); // "파이프 입력 스트림 객체"공개 pipedReader getReader () {return in; } @override public void run () {readMessageOnce (); // readMessAgeContinued (); } // "파이프 입력 스트림"에서 한 번 읽기 "public void void readMessageOnce () {// buf의 크기는 2048 자이지만"파이프 입력 스트림 "에서 최대 1024 자만 읽습니다. // "파이프 입력 스트림"의 버퍼 크기는 기본적으로 1024 자이기 때문입니다. char [] buf = new char [2048]; try {int len = in.read (buf); System.out.println (새 문자열 (buf, 0, len)); 넣다(); } catch (ioexception e) {e.printstacktrace (); }} // "파이프 입력 스트림"에서> 1024 문자를 읽을 때 공개 void readMessageContinued () {int total = 0; while (true) {char [] buf = new char [1024]; try {int len = in.read (buf); 총 += 렌; System.out.println (새 문자열 (buf, 0, len)); // 읽은 총 문자 수가> 1024 인 경우 루프가 종료됩니다. IF (총> 1024) 파손; } catch (ioexception e) {e.printstacktrace (); }} try {in.close (); } catch (ioexception e) {e.printstacktrace (); }}} Sender.java 코드는 다음과 같습니다.
import java.io.ioexception; import java.io.pipedwriter; @SuppressWarnings ( "All")/ *** Sender Thread*/ Public Class 발신자는 스레드 {// 파이프 라인 출력 스트림 객체를 확장합니다. // "pipedReader"객체에 바인딩됩니다. // 이것은 데이터를 "PipedReader"의 데이터로 전송할 수있게 한 다음 사용자는 "PipedReader"에서 데이터를 읽을 수 있습니다. 개인 파이프 라이터 out = New PipedWriter (); // "파이프 출력 스트림"객체 공개 PipedWriter getWriter () {return out; } @override public void run () {writesHortMessage (); // writeLongMessage (); } // "파이프 출력 스트림"에 짧은 메시지를 씁니다. try {out.write (strinfo.tochararray ()); out.close (); } catch (ioexception e) {e.printstacktrace (); }} // "파이프 출력 스트림"에 긴 메시지를 씁니다. private void writeLongMessage () {StringBuilder sb = new StringBuilder (); // for loop for (int i = 0; i <102; i ++) sb.append ( "0123456789")를 통해 1020자를 쓰십시오. // 26자를 더 씁니다. sb.append ( "abcdefghijklmnopqrstuvwxyz"); // str의 총 길이는 1020+26 = 1046 문자열 str = sb.tostring (); {// 1046 문자를 "파이프 출력 스트림"out.write (str)에 쓰십시오. out.close (); } catch (ioexception e) {e.printstacktrace (); }}} pipetest.java 코드는 다음과 같습니다.
import java.io.pipedReader; import java.io.pipedwriter; import java.io.ioexception; @suppresswarnings ( "all") / *** 파이프 라인 입력 스트림 및 파이프 라인 출력 스트림을위한 대화식 프로그램* / public class pipetest {public static void main (] args) {보내기 t1 = 새로운 송신기 (); 수신기 t2 = 새로운 수신기 (); pipedwriter out = t1.getWriter (); pipedReader in = t2.getReader (); {// 파이프 연결을 시도하십시오. 다음 두 문장의 본질은 동일합니다. //out.connect(in); in.connect (out); /** * 스레드 클래스의 시작 방법 : * 스레드가 실행을 시작합니다. Java Virtual Machine은 Thread의 실행 메소드를 호출합니다. * 결과적으로 두 개의 스레드가 동시에 실행됩니다. 현재 스레드 (통화에서 시작 메소드로 반환 됨)와 다른 스레드 (실행 메소드 실행). * 스레드를 여러 번 시작하는 것은 불법입니다. 특히 스레드가 실행 된 경우 다시 시작할 수 없습니다. */ t1.start (); t2.start (); } catch (ioexception e) {e.printstacktrace (); }}} 실행 결과 :
이것은 짧은 메시지입니다
결과 설명 :
(1)
in.connect (out);
그 기능은 "파이프 입력 스트림"과 "파이프 출력 스트림"을 연관시키는 것입니다. PipedWriter.java 및 PipedReader.java에서 Connect ()의 소스 코드를 확인하십시오. 우리는 알고 있습니다. Connect (in); in.connect (out)와 동일합니다.
(2)
t1.start (); // "sender"스레드 t2.start ()를 시작합니다. // "수신기"스레드를 시작합니다
먼저 sender.java의 소스 코드를 확인하고 스레드가 시작된 후 run () 함수를 실행합니다. sender.java의 run ()에서 writesHortMessage ()를 호출합니다.
writesHortMessage ()의 함수; "파이프 출력 스트림"에 "이것은 짧은 메시지"데이터를 작성하는 것입니다. 이 데이터는 "파이프 입력 스트림"에 의해 수신됩니다. 이것이 어떻게 달성되는지 봅시다.
먼저 Write of Write 코드 (char char. pipedwriter.java는 Writer.java의 상속자; Writer.java의 소스 코드 (char c [])를 다음과 같습니다.
public void write (char cbuf [])는 ioexception {write (cbuf, 0, cbuf.length);}
실제로, write (char c [])은 pipedwriter.java의 호출 쓰기 (char c [], int off, int len) 함수입니다. 쓰기 소스 코드 (char c [], int off, int len)를 살펴보면 sink.receive (cbuf, off, len)를 호출한다는 것을 알았습니다. 수신 (char c [], int off, int len)의 정의를 살펴보면 Sink.Receive (CBUF, OFF, LEN)는 "파이프 출력 스트림"의 데이터를 "파이프 입력 스트림"의 버퍼에 저장하는 것임을 알고 있습니다. "파이프 입력 스트림"의 버퍼 버퍼의 기본 크기는 1024 자입니다.
이 시점에서 우리는 다음과 같이 알고 있습니다. t1.start ()는 발신자 스레드를 시작하고 발신자 스레드는 "파이프 출력 스트림"에 "짧은 메시지"데이터를 작성합니다. "파이프 출력 스트림"은 데이터를 "파이프 입력 스트림"으로 전송합니다. 즉, "파이프 입력 스트림"의 버퍼에 저장됩니다.
다음으로 "사용자가 '파이프 입력 스트림'의 버퍼에서 데이터를 읽는 방법"을 살펴 봅니다. 이것은 실제로 수신기 스레드의 동작입니다.
t2.start ()는 수신기 스레드를 시작하여 수신기.java run () 함수를 실행합니다. 수신자의 소스 코드를 살펴보면 run ()가 readMessageOnce ()를 호출한다는 것을 알고 있습니다.
readMessageOnce ()는 in.read (buf)를 호출하여 "파이프 입력 스트림"의 데이터를 읽고 BUF에 저장해야합니다.
위의 분석을 통해, 우리는 이미 "파이프 입력 스트림"의 버퍼에있는 데이터가 "짧은 메시지"라는 것을 이미 알고 있습니다. 따라서 BUF의 데이터는 "이것은 짧은 메시지입니다"입니다.
파이프 라인의 이해를 심화시키기 위해. 우리는 다음 두 가지 작은 실험을 계속할 것입니다.
실험 1 : sender.java를 수정합니다
할 것이다
public void run () {writesHortMessage (); // writeLongMessage ();} 수정
public void run () {// writesHortMessage (); writelongmessage ();} 프로그램을 실행하십시오. 작업 결과는 다음과 같습니다.
이것으로부터, 우리는 프로그램이 잘못 실행된다는 것을 알 수 있습니다! 제외 java.io.ioexception : 파이프가 닫히십시오
왜 이런 일이 일어나고 있습니까?
프로그램 흐름을 분석하겠습니다.
(1) pipetest에서는 in.connect (Out)를 통해 입력 및 출력 파이프 라인을 연결합니다. 그런 다음 두 개의 스레드를 시작하십시오. t1.start ()는 스레드 송신기를 시작하고 t2.start ()는 스레드 수신기를 시작합니다.
(2) 송신자 스레드가 시작된 후 데이터는 WriteLongMessage ()를 통해 "출력 파이프 라인"에 기록되고 out.write (str.tochararray ())는 총 1046자를 씁니다. PipedWriter의 소스 코드에 따르면 PipedWriter의 write () 함수는 PipedReader의 수신 () 함수를 호출합니다. PipedReader의 수신 () 함수를 살펴보면 PipedReader가 허용 된 데이터 버퍼를 저장한다는 것을 알고 있습니다. 수신자 () 함수를주의 깊게 관찰하면 다음 코드가 있습니다.
while (in == out) {if ((readside! = null) &&! readside.isalive ()) {새로운 ioexception ( "파이프 파손"); } / * full : 대기 독자를 차기 * / notifyall (); try {WAIT (1000); } catch (InterruptedException ex) {throw new java.io.interruptedioexception (); }} 인과 아웃의 초기 값은 각각 = -1, out = 0입니다. 위의 while (in == out)과 결합합니다. 우리는 그 의미가 캐릭터가 파이프 라인에 기록 될 때마다 == out 조건이 충족된다는 것을 알고 있습니다. 그런 다음 "파이프 라인을 읽는 스레드"를 깨우기 위해 NotifyAll ()이 호출됩니다.
즉, 캐릭터가 파이프 라인에 기록 될 때마다 다른 스레드가 읽을 때까지 차단하고 기다립니다.
그러나 PipedReader의 버퍼의 기본 크기는 1024입니다! 그러나 현재 1046 개의 데이터가 작성됩니다! 따라서 한 번에 최대 1024자를 쓸 수 있습니다.
(03) 수신기 스레드가 시작되면 Pipeline Input Stream을 읽기 위해 ReadMessageOnce ()가 호출됩니다. 독서 1024자가 수행되며, Close ()가 파이프를 닫도록 요청합니다.
(02) 및 (03)의 분석에서 발신자는 파이프 라인에 1046자를 써야한다는 것을 알 수 있습니다. 그중에서도 첫 1024 자 (버퍼 용량은 1024)는 정상적으로 작성할 수 있으며, 하나는 각각의 쓰기에 대해 읽습니다. 1025 문자가 작성되면 PipedWriter.java의 write ()가 여전히 순서대로 호출됩니다. 그런 다음 PipedReader.java에서 수신자 ()가 호출됩니다. PipedReader.java에서 수신 (int c) 함수가 결국 호출됩니다. 현재 파이프 라인 입력 스트림이 닫혔습니다.
우리는 문제를 해결하기 위해 "테스트 하나"를 계속 수정합니다.
실험 2 : "실험 1"을 기준으로 수신기 .java를 계속 수정하십시오.
public void run () {readMessageOnce (); // readMessageContinued ();} 수정
public void run () {// readMessageOnce (); readmessagecontinued ();} 현재 프로그램은 정상적으로 실행될 수 있습니다. 실행 결과는 다음과 같습니다.