code: @Override
      public synchronized int read() throws IOException {
          if (!isConnected) {
              throw new IOException("Not connected");
          }
          if (buffer == null) {
              throw new IOException("InputStream is closed");
          }
  
          if (isClosed && in == -1) {
              // write end closed and no more need to read
              return -1;
          }
  
          /**
           * Set the last thread to be reading on this PipedInputStream. If
           * lastReader dies while someone is waiting to write an IOException of
           * "Pipe broken" will be thrown in receive()
           */
          lastReader = Thread.currentThread();
          try {
              int attempts = 3;
              while (in == -1) {
                  // Are we at end of stream?
                  if (isClosed) {
                      return -1;
                  }
                  if ((attempts-- <= 0) && lastWriter != null && !lastWriter.isAlive()) {
                      throw new IOException("Pipe broken");
                  }
                  // Notify callers of receive()
                  notifyAll();
                  wait(1000);
              }
          } catch (InterruptedException e) {
              IoUtils.throwInterruptedIoException();
          }
  
          int result = buffer[out++] & 0xff;
          if (out == buffer.length) {
              out = 0;
          }
          if (out == in) {
              // empty buffer
              in = -1;
              out = 0;
          }
  
          // let blocked writers write to the newly available buffer space
          notifyAll();
  
          return result;
      }
docs: Reads a single byte from this stream and returns it as an integer in the
  range from 0 to 255. Returns -1 if the end of this stream has been
  reached. If there is no data in the pipe, this method blocks until data
  is available, the end of the stream is detected or an exception is
  thrown.
  <p>
  Separate threads should be used to read from a {@code PipedInputStream}
  and to write to the connected {@link PipedOutputStream}. If the same
  thread is used, a deadlock may occur.
  
  @return the byte read or -1 if the end of the source stream has been
  reached.
  @throws IOException
  if this stream is closed or not connected to an output
  stream, or if the thread writing to the connected output
  stream is no longer alive.