@@ -176,23 +176,15 @@ public class IOUtils {
176176 public static final String LINE_SEPARATOR_WINDOWS = StandardLineSeparator .CRLF .getString ();
177177
178178 /**
179- * The default buffer to use for the skip() methods .
179+ * Internal byte array buffer .
180180 */
181- private static final byte [] SKIP_BYTE_BUFFER = byteArray ();
182-
183- // Allocated in the relevant skip method if necessary.
184- /*
185- * These buffers are static and are shared between threads.
186- * This is possible because the buffers are write-only - the contents are never read.
187- *
188- * N.B. there is no need to synchronize when creating these because:
189- * - we don't care if the buffer is created multiple times (the data is ignored)
190- * - we always use the same size buffer, so if it it is recreated it will still be OK
191- * (if the buffer size were variable, we would need to synch. to ensure some other thread
192- * did not create a smaller one)
181+ private static final ThreadLocal <byte []> SKIP_BYTE_BUFFER = ThreadLocal .withInitial (() -> byteArray ());
182+
183+ /**
184+ * Internal byte array buffer.
193185 */
194- private static char [] SKIP_CHAR_BUFFER ;
195-
186+ private static final ThreadLocal < char []> SKIP_CHAR_BUFFER = ThreadLocal . withInitial (() -> charArray ()) ;
187+
196188 /**
197189 * Returns the given InputStream if it is already a {@link BufferedInputStream}, otherwise creates a
198190 * BufferedInputStream from the given InputStream.
@@ -344,6 +336,29 @@ public static byte[] byteArray(final int size) {
344336 return new byte [size ];
345337 }
346338
339+ /**
340+ * Returns a new char array of size {@link #DEFAULT_BUFFER_SIZE}.
341+ *
342+ * @return a new char array of size {@link #DEFAULT_BUFFER_SIZE}.
343+ * @since 2.9.0
344+ */
345+ private static char [] charArray () {
346+ return charArray (DEFAULT_BUFFER_SIZE );
347+ }
348+
349+ /**
350+ * Returns a new char array of the given size.
351+ *
352+ * TODO Consider guarding or warning against large allocations...
353+ *
354+ * @param size array size.
355+ * @return a new char array of the given size.
356+ * @since 2.9.0
357+ */
358+ private static char [] charArray (final int size ) {
359+ return new char [size ];
360+ }
361+
347362 /**
348363 * Closes the given {@link Closeable} as a null-safe operation.
349364 *
@@ -755,7 +770,7 @@ public static void closeQuietly(final Writer writer) {
755770 */
756771 public static long consume (final InputStream input )
757772 throws IOException {
758- return copyLarge (input , NullOutputStream .NULL_OUTPUT_STREAM , SKIP_BYTE_BUFFER );
773+ return copyLarge (input , NullOutputStream .NULL_OUTPUT_STREAM , getByteArray () );
759774 }
760775
761776 /**
@@ -783,7 +798,9 @@ public static boolean contentEquals(final InputStream input1, final InputStream
783798 return false ;
784799 }
785800
786- final byte [] array1 = byteArray ();
801+ // reuse one
802+ final byte [] array1 = getByteArray ();
803+ // allocate another
787804 final byte [] array2 = byteArray ();
788805 int pos1 ;
789806 int pos2 ;
@@ -839,8 +856,10 @@ public static boolean contentEquals(final Reader input1, final Reader input2) th
839856 return false ;
840857 }
841858
842- final char [] array1 = new char [DEFAULT_BUFFER_SIZE ];
843- final char [] array2 = new char [DEFAULT_BUFFER_SIZE ];
859+ // reuse one
860+ final char [] array1 = getCharArray ();
861+ // but allocate another
862+ final char [] array2 = charArray ();
844863 int pos1 ;
845864 int pos2 ;
846865 int count1 ;
@@ -1318,7 +1337,7 @@ public static long copyLarge(final InputStream inputStream, final OutputStream o
13181337 */
13191338 public static long copyLarge (final InputStream input , final OutputStream output , final long inputOffset ,
13201339 final long length ) throws IOException {
1321- return copyLarge (input , output , inputOffset , length , byteArray ());
1340+ return copyLarge (input , output , inputOffset , length , getByteArray ());
13221341 }
13231342
13241343 /**
@@ -1387,7 +1406,7 @@ public static long copyLarge(final InputStream input, final OutputStream output,
13871406 * @since 1.3
13881407 */
13891408 public static long copyLarge (final Reader reader , final Writer writer ) throws IOException {
1390- return copyLarge (reader , writer , new char [ DEFAULT_BUFFER_SIZE ] );
1409+ return copyLarge (reader , writer , getCharArray () );
13911410 }
13921411
13931412 /**
@@ -1436,7 +1455,7 @@ public static long copyLarge(final Reader reader, final Writer writer, final cha
14361455 */
14371456 public static long copyLarge (final Reader reader , final Writer writer , final long inputOffset , final long length )
14381457 throws IOException {
1439- return copyLarge (reader , writer , inputOffset , length , new char [ DEFAULT_BUFFER_SIZE ] );
1458+ return copyLarge (reader , writer , inputOffset , length , getCharArray () );
14401459 }
14411460
14421461 /**
@@ -1484,6 +1503,24 @@ public static long copyLarge(final Reader reader, final Writer writer, final lon
14841503 return totalRead ;
14851504 }
14861505
1506+ /**
1507+ * Gets the thread local byte array.
1508+ *
1509+ * @return the thread local byte array.
1510+ */
1511+ static byte [] getByteArray () {
1512+ return SKIP_BYTE_BUFFER .get ();
1513+ }
1514+
1515+ /**
1516+ * Gets the thread local char array.
1517+ *
1518+ * @return the thread local char array.
1519+ */
1520+ static char [] getCharArray () {
1521+ return SKIP_CHAR_BUFFER .get ();
1522+ }
1523+
14871524 /**
14881525 * Returns the length of the given array in a null-safe manner.
14891526 *
@@ -2113,7 +2150,8 @@ public static long skip(final InputStream input, final long toSkip) throws IOExc
21132150 long remain = toSkip ;
21142151 while (remain > 0 ) {
21152152 // See https://issues.apache.org/jira/browse/IO-203 for why we use read() rather than delegating to skip()
2116- final long n = input .read (SKIP_BYTE_BUFFER , 0 , (int ) Math .min (remain , SKIP_BYTE_BUFFER .length ));
2153+ final byte [] byteArray = getByteArray ();
2154+ final long n = input .read (byteArray , 0 , (int ) Math .min (remain , byteArray .length ));
21172155 if (n < 0 ) { // EOF
21182156 break ;
21192157 }
@@ -2138,11 +2176,11 @@ public static long skip(final ReadableByteChannel input, final long toSkip) thro
21382176 if (toSkip < 0 ) {
21392177 throw new IllegalArgumentException ("Skip count must be non-negative, actual: " + toSkip );
21402178 }
2141- final ByteBuffer skipByteBuffer = ByteBuffer .allocate ((int ) Math .min (toSkip , SKIP_BYTE_BUFFER . length ));
2179+ final ByteBuffer skipByteBuffer = ByteBuffer .allocate ((int ) Math .min (toSkip , DEFAULT_BUFFER_SIZE ));
21422180 long remain = toSkip ;
21432181 while (remain > 0 ) {
21442182 skipByteBuffer .position (0 );
2145- skipByteBuffer .limit ((int ) Math .min (remain , SKIP_BYTE_BUFFER . length ));
2183+ skipByteBuffer .limit ((int ) Math .min (remain , DEFAULT_BUFFER_SIZE ));
21462184 final int n = input .read (skipByteBuffer );
21472185 if (n == EOF ) {
21482186 break ;
@@ -2177,18 +2215,11 @@ public static long skip(final Reader reader, final long toSkip) throws IOExcepti
21772215 if (toSkip < 0 ) {
21782216 throw new IllegalArgumentException ("Skip count must be non-negative, actual: " + toSkip );
21792217 }
2180- /*
2181- * N.B. no need to synchronize this because: - we don't care if the buffer is created multiple times (the data
2182- * is ignored) - we always use the same size buffer, so if it it is recreated it will still be OK (if the buffer
2183- * size were variable, we would need to synch. to ensure some other thread did not create a smaller one)
2184- */
2185- if (SKIP_CHAR_BUFFER == null ) {
2186- SKIP_CHAR_BUFFER = new char [SKIP_BYTE_BUFFER .length ];
2187- }
21882218 long remain = toSkip ;
21892219 while (remain > 0 ) {
21902220 // See https://issues.apache.org/jira/browse/IO-203 for why we use read() rather than delegating to skip()
2191- final long n = reader .read (SKIP_CHAR_BUFFER , 0 , (int ) Math .min (remain , SKIP_BYTE_BUFFER .length ));
2221+ final char [] charArray = getCharArray ();
2222+ final long n = reader .read (charArray , 0 , (int ) Math .min (remain , charArray .length ));
21922223 if (n < 0 ) { // EOF
21932224 break ;
21942225 }
0 commit comments