读文件并发上不去问题 org.apache.commons.io.FileUtils.readFileToByteArray()

apache的技术毋庸置疑,但使用时如果不加注意,坑还是有的。
org.apache.commons.io.FileUtils.readFileToByteArray() 方法能将文件转换为byte数组,让我们看一下它的源码

/**
 * Reads the contents of a file into a byte array.
 * The file is always closed.
 *
 * @param file  the file to read, must not be {@code null}
 * @return the file contents, never {@code null}
 * @throws IOException in case of an I/O error
 * @since 1.1
 */
  public static byte[] readFileToByteArray(File file) throws IOException {
    InputStream in = null;
    try {
        in = openInputStream(file);
        return IOUtils.toByteArray(in, file.length());
    } finally {
        IOUtils.closeQuietly(in);
    }
}

可以看到此方法先将文件转换成InputStream in = openInputStream(file);
这个方法只是进行了简单的校验和检查,我们不必深究,下边returen尾调用中的方法
IOUtils.toByteArray(in, file.length()) 是重点,可以看到参数包含一个文件流和一个long型的文件长度,然后我们看一下这个方法的源码及注释

 /**
     * Get contents of an <code>InputStream</code> as a <code>byte[]</code>.
     * Use this method instead of <code>toByteArray(InputStream)</code>
     * when <code>InputStream</code> size is known.
     * <b>NOTE:</b> the method checks that the length can safely be cast to an int without truncation
     * before using {@link IOUtils#toByteArray(java.io.InputStream, int)} to read into the byte array.
     * (Arrays can have no more than Integer.MAX_VALUE entries anyway)
     * 
     * @param input the <code>InputStream</code> to read from
     * @param size the size of <code>InputStream</code>
     * @return the requested byte array
     * @throws IOException if an I/O error occurs or <code>InputStream</code> size differ from parameter size
     * @throws IllegalArgumentException if size is less than zero or size is greater than Integer.MAX_VALUE
     * @see IOUtils#toByteArray(java.io.InputStream, int)
     * @since 2.1
     */
public static byte[] toByteArray(InputStream input, long size) throws IOException {

  if(size > Integer.MAX_VALUE) {
      throw new IllegalArgumentException("Size cannot be greater than Integer max value: " + size);
  }

  return toByteArray(input, (int) size);
}

问题就出现在这里,首先它是static修饰的方法,作为一个程序员static可谓是见久不怪了,static修饰符修的方法在程序初始化时就会被加载,也是唯一一次加载,使得当前方法在内存中只有一个实例,极大地节约内存开销,但是在高并发的情况下,这个方法久可能会造成一个线程的数据还没读完,就被另一个线程覆盖了。所以这个方法在并发要求严格的项目中不适用。加锁更会导致项目在这个方法中运行缓慢。拒绝使用时最佳的解决方案。

然后说一说这个方法。

可能我们以为传入的是long类型,处理算法就真的是处理long类型才装的下的文件大小的大文件的方法,然而很搞笑,这个方法先把传入的 file.length() 检查一下,是否超过了四个字节的 Integer 型变量,超过了报异常,如果在Integer范围内,则尾调用 toByteArray(input, (int) size); 也就是真正将fileInoutStream 转换为byte数组的方法,这个方法没什么高深的地方,进行一系列的检查,然后调用java底层的 read() 方法,和我们直接调用 file.read(); 类似。

同事被并发问题搞了好久,我帮忙解决一下,发现一个坑,特此发文帮广大码友避坑。

猜你喜欢

转载自blog.csdn.net/w4187402/article/details/85257472
今日推荐