< prev index next >

src/java.base/share/classes/java/io/BufferedReader.java

Print this page
@@ -23,18 +23,18 @@
   * questions.
   */
  
  package java.io;
  
- 
  import java.util.Iterator;
  import java.util.NoSuchElementException;
  import java.util.Objects;
  import java.util.Spliterator;
  import java.util.Spliterators;
  import java.util.stream.Stream;
  import java.util.stream.StreamSupport;
+ import jdk.internal.misc.InternalLock;
  
  /**
   * Reads text from a character-input stream, buffering characters so as to
   * provide for the efficient reading of characters, arrays, and lines.
   *

@@ -67,11 +67,10 @@
   * @author      Mark Reinhold
   * @since       1.1
   */
  
  public class BufferedReader extends Reader {
- 
      private Reader in;
  
      private char[] cb;
      private int nChars, nextChar;
  

@@ -86,22 +85,22 @@
      /** The skipLF flag when the mark was set */
      private boolean markedSkipLF = false;
  
      private static int defaultCharBufferSize = 8192;
      private static int defaultExpectedLineLength = 80;
- 
+     
      /**
       * Creates a buffering character-input stream that uses an input buffer of
       * the specified size.
       *
       * @param  in   A Reader
       * @param  sz   Input-buffer size
       *
       * @throws IllegalArgumentException  If {@code sz <= 0}
       */
      public BufferedReader(Reader in, int sz) {
-         super(in);
+         Objects.requireNonNull(in);
          if (sz <= 0)
              throw new IllegalArgumentException("Buffer size <= 0");
          this.in = in;
          cb = new char[sz];
          nextChar = nChars = 0;

@@ -174,27 +173,41 @@
       *         0 to 65535 ({@code 0x00-0xffff}), or -1 if the
       *         end of the stream has been reached
       * @throws     IOException  If an I/O error occurs
       */
      public int read() throws IOException {
-         synchronized (lock) {
-             ensureOpen();
-             for (;;) {
-                 if (nextChar >= nChars) {
-                     fill();
-                     if (nextChar >= nChars)
-                         return -1;
-                 }
-                 if (skipLF) {
-                     skipLF = false;
-                     if (cb[nextChar] == '\n') {
-                         nextChar++;
-                         continue;
-                     }
+         Object lock = this.lock;
+         if (lock instanceof InternalLock locker) {
+             locker.lock();
+             try {
+                 return lockedRead();
+             } finally {
+                 locker.unlock();
+             }
+         } else {
+             synchronized (lock) {
+                 return lockedRead();
+             }
+         }
+     }
+ 
+     private int lockedRead() throws IOException {
+         ensureOpen();
+         for (;;) {
+             if (nextChar >= nChars) {
+                 fill();
+                 if (nextChar >= nChars)
+                     return -1;
+             }
+             if (skipLF) {
+                 skipLF = false;
+                 if (cb[nextChar] == '\n') {
+                     nextChar++;
+                     continue;
                  }
-                 return cb[nextChar++];
              }
+             return cb[nextChar++];
          }
      }
  
      /**
       * Reads characters into a portion of an array, reading from the underlying

@@ -275,28 +288,42 @@
       *
       * @throws     IndexOutOfBoundsException {@inheritDoc}
       * @throws     IOException  {@inheritDoc}
       */
      public int read(char[] cbuf, int off, int len) throws IOException {
-         synchronized (lock) {
-             ensureOpen();
-             Objects.checkFromIndexSize(off, len, cbuf.length);
-             if (len == 0) {
-                 return 0;
+         Object lock = this.lock;
+         if (lock instanceof InternalLock locker) {
+             locker.lock();
+             try {
+                 return lockedRead(cbuf, off, len);
+             } finally {
+                 locker.unlock();
              }
- 
-             int n = read1(cbuf, off, len);
-             if (n <= 0) return n;
-             while ((n < len) && in.ready()) {
-                 int n1 = read1(cbuf, off + n, len - n);
-                 if (n1 <= 0) break;
-                 n += n1;
+         } else {
+             synchronized (lock) {
+                 return lockedRead(cbuf, off, len);
              }
-             return n;
          }
      }
  
+     private int lockedRead(char[] cbuf, int off, int len) throws IOException {
+         ensureOpen();
+         Objects.checkFromIndexSize(off, len, cbuf.length);
+         if (len == 0) {
+             return 0;
+         }
+ 
+         int n = read1(cbuf, off, len);
+         if (n <= 0) return n;
+         while ((n < len) && in.ready()) {
+             int n1 = read1(cbuf, off + n, len - n);
+             if (n1 <= 0) break;
+             n += n1;
+         }
+         return n;
+     }
+ 
      /**
       * Reads a line of text.  A line is considered to be terminated by any one
       * of a line feed ('\n'), a carriage return ('\r'), a carriage return
       * followed immediately by a line feed, or by reaching the end-of-file
       * (EOF).

@@ -312,71 +339,85 @@
       * @see        java.io.LineNumberReader#readLine()
       *
       * @throws     IOException  If an I/O error occurs
       */
      String readLine(boolean ignoreLF, boolean[] term) throws IOException {
+         Object lock = this.lock;
+         if (lock instanceof InternalLock locker) {
+             locker.lock();
+             try {
+                 return lockedReadLine(ignoreLF, term);
+             } finally {
+                 locker.unlock();
+             }
+         } else {
+             synchronized (lock) {
+                 return lockedReadLine(ignoreLF, term);
+             }
+         }
+     }
+ 
+     private String lockedReadLine(boolean ignoreLF, boolean[] term) throws IOException {
          StringBuilder s = null;
          int startChar;
  
-         synchronized (lock) {
-             ensureOpen();
-             boolean omitLF = ignoreLF || skipLF;
-             if (term != null) term[0] = false;
+         ensureOpen();
+         boolean omitLF = ignoreLF || skipLF;
+         if (term != null) term[0] = false;
  
-         bufferLoop:
-             for (;;) {
+       bufferLoop:
+         for (;;) {
  
-                 if (nextChar >= nChars)
-                     fill();
-                 if (nextChar >= nChars) { /* EOF */
-                     if (s != null && s.length() > 0)
-                         return s.toString();
-                     else
-                         return null;
-                 }
-                 boolean eol = false;
-                 char c = 0;
-                 int i;
+             if (nextChar >= nChars)
+                 fill();
+             if (nextChar >= nChars) { /* EOF */
+                 if (s != null && s.length() > 0)
+                     return s.toString();
+                 else
+                     return null;
+             }
+             boolean eol = false;
+             char c = 0;
+             int i;
  
-                 /* Skip a leftover '\n', if necessary */
-                 if (omitLF && (cb[nextChar] == '\n'))
-                     nextChar++;
-                 skipLF = false;
-                 omitLF = false;
- 
-             charLoop:
-                 for (i = nextChar; i < nChars; i++) {
-                     c = cb[i];
-                     if ((c == '\n') || (c == '\r')) {
-                         if (term != null) term[0] = true;
-                         eol = true;
-                         break charLoop;
-                     }
+             /* Skip a leftover '\n', if necessary */
+             if (omitLF && (cb[nextChar] == '\n'))
+                 nextChar++;
+             skipLF = false;
+             omitLF = false;
+ 
+           charLoop:
+             for (i = nextChar; i < nChars; i++) {
+                 c = cb[i];
+                 if ((c == '\n') || (c == '\r')) {
+                     if (term != null) term[0] = true;
+                     eol = true;
+                     break charLoop;
                  }
+             }
  
-                 startChar = nextChar;
-                 nextChar = i;
- 
-                 if (eol) {
-                     String str;
-                     if (s == null) {
-                         str = new String(cb, startChar, i - startChar);
-                     } else {
-                         s.append(cb, startChar, i - startChar);
-                         str = s.toString();
-                     }
-                     nextChar++;
-                     if (c == '\r') {
-                         skipLF = true;
-                     }
-                     return str;
-                 }
+             startChar = nextChar;
+             nextChar = i;
  
-                 if (s == null)
-                     s = new StringBuilder(defaultExpectedLineLength);
-                 s.append(cb, startChar, i - startChar);
+             if (eol) {
+                 String str;
+                 if (s == null) {
+                     str = new String(cb, startChar, i - startChar);
+                 } else {
+                     s.append(cb, startChar, i - startChar);
+                     str = s.toString();
+                 }
+                 nextChar++;
+                 if (c == '\r') {
+                     skipLF = true;
+                 }
+                 return str;
              }
+ 
+             if (s == null)
+                 s = new StringBuilder(defaultExpectedLineLength);
+             s.append(cb, startChar, i - startChar);
          }
      }
  
      /**
       * Reads a line of text.  A line is considered to be terminated by any one

@@ -401,69 +442,97 @@
       */
      public long skip(long n) throws IOException {
          if (n < 0L) {
              throw new IllegalArgumentException("skip value is negative");
          }
-         synchronized (lock) {
-             ensureOpen();
-             long r = n;
-             while (r > 0) {
-                 if (nextChar >= nChars)
-                     fill();
-                 if (nextChar >= nChars) /* EOF */
-                     break;
-                 if (skipLF) {
-                     skipLF = false;
-                     if (cb[nextChar] == '\n') {
-                         nextChar++;
-                     }
-                 }
-                 long d = nChars - nextChar;
-                 if (r <= d) {
-                     nextChar += r;
-                     r = 0;
-                     break;
-                 }
-                 else {
-                     r -= d;
-                     nextChar = nChars;
+         Object lock = this.lock;
+         if (lock instanceof InternalLock locker) {
+             locker.lock();
+             try {
+                 return lockedSkip(n);
+             } finally {
+                 locker.unlock();
+             }
+         } else {
+             synchronized (lock) {
+                 return lockedSkip(n);
+             }
+         }
+     }
+ 
+     private long lockedSkip(long n) throws IOException {
+         ensureOpen();
+         long r = n;
+         while (r > 0) {
+             if (nextChar >= nChars)
+                 fill();
+             if (nextChar >= nChars) /* EOF */
+                 break;
+             if (skipLF) {
+                 skipLF = false;
+                 if (cb[nextChar] == '\n') {
+                     nextChar++;
                  }
              }
-             return n - r;
+             long d = nChars - nextChar;
+             if (r <= d) {
+                 nextChar += r;
+                 r = 0;
+                 break;
+             }
+             else {
+                 r -= d;
+                 nextChar = nChars;
+             }
          }
+         return n - r;
      }
  
      /**
       * Tells whether this stream is ready to be read.  A buffered character
       * stream is ready if the buffer is not empty, or if the underlying
       * character stream is ready.
       *
       * @throws     IOException  If an I/O error occurs
       */
      public boolean ready() throws IOException {
-         synchronized (lock) {
-             ensureOpen();
+         Object lock = this.lock;
+         if (lock instanceof InternalLock locker) {
+             locker.lock();
+             try {
+                 return lockedReady();
+             } finally {
+                 locker.unlock();
+             }
+         } else {
+             synchronized (lock) {
+                 return lockedReady();
+             }
+         }
+     }
  
-             /*
-              * If newline needs to be skipped and the next char to be read
-              * is a newline character, then just skip it right away.
+     private boolean lockedReady() throws IOException {
+         ensureOpen();
+ 
+         /*
+          * If newline needs to be skipped and the next char to be read
+          * is a newline character, then just skip it right away.
+          */
+         if (skipLF) {
+             /* Note that in.ready() will return true if and only if the next
+              * read on the stream will not block.
               */
-             if (skipLF) {
-                 /* Note that in.ready() will return true if and only if the next
-                  * read on the stream will not block.
-                  */
-                 if (nextChar >= nChars && in.ready()) {
-                     fill();
-                 }
-                 if (nextChar < nChars) {
-                     if (cb[nextChar] == '\n')
-                         nextChar++;
-                     skipLF = false;
-                 }
+             if (nextChar >= nChars && in.ready()) {
+                 fill();
+             }
+             if (nextChar < nChars) {
+                 if (cb[nextChar] == '\n')
+                     nextChar++;
+                 skipLF = false;
              }
-             return (nextChar < nChars) || in.ready();
          }
+         return (nextChar < nChars) || in.ready();
      }
  
      /**
       * Tells whether this stream supports the mark() operation, which it does.
       */

@@ -489,46 +558,88 @@
       */
      public void mark(int readAheadLimit) throws IOException {
          if (readAheadLimit < 0) {
              throw new IllegalArgumentException("Read-ahead limit < 0");
          }
-         synchronized (lock) {
-             ensureOpen();
-             this.readAheadLimit = readAheadLimit;
-             markedChar = nextChar;
-             markedSkipLF = skipLF;
+         Object lock = this.lock;
+         if (lock instanceof InternalLock locker) {
+             locker.lock();
+             try {
+                 lockedMark(readAheadLimit);
+             } finally {
+                 locker.unlock();
+             }
+         } else {
+             synchronized (lock) {
+                 lockedMark(readAheadLimit);
+             }
          }
      }
  
+     private void lockedMark(int readAheadLimit) throws IOException {
+         ensureOpen();
+         this.readAheadLimit = readAheadLimit;
+         markedChar = nextChar;
+         markedSkipLF = skipLF;
+     }
+ 
      /**
       * Resets the stream to the most recent mark.
       *
       * @throws     IOException  If the stream has never been marked,
       *                          or if the mark has been invalidated
       */
      public void reset() throws IOException {
-         synchronized (lock) {
-             ensureOpen();
-             if (markedChar < 0)
-                 throw new IOException((markedChar == INVALIDATED)
-                                       ? "Mark invalid"
-                                       : "Stream not marked");
-             nextChar = markedChar;
-             skipLF = markedSkipLF;
+         Object lock = this.lock;
+         if (lock instanceof InternalLock locker) {
+             locker.lock();
+             try {
+                 lockedReset();
+             } finally {
+                 locker.unlock();
+             }
+         } else {
+             synchronized (lock) {
+                 lockedReset();
+             }
          }
      }
  
+     private void lockedReset() throws IOException {
+         ensureOpen();
+         if (markedChar < 0)
+             throw new IOException((markedChar == INVALIDATED)
+                                   ? "Mark invalid"
+                                   : "Stream not marked");
+         nextChar = markedChar;
+         skipLF = markedSkipLF;
+     }
+ 
      public void close() throws IOException {
-         synchronized (lock) {
-             if (in == null)
-                 return;
+         Object lock = this.lock;
+         if (lock instanceof InternalLock locker) {
+             locker.lock();
              try {
-                 in.close();
+                 lockedClose();
              } finally {
-                 in = null;
-                 cb = null;
+                 locker.unlock();
              }
+         } else {
+             synchronized (lock) {
+                 lockedClose();
+             }
+         }
+     }
+ 
+     private void lockedClose() throws IOException {
+         if (in == null)
+             return;
+         try {
+             in.close();
+         } finally {
+             in = null;
+             cb = null;
          }
      }
  
      /**
       * Returns a {@code Stream}, the elements of which are lines read from
< prev index next >