最开始 使用的是传统的方式, 使用的 是 InputStream 里面的 read
使用Input Strem 进行读取, 花了121 s
//use InputStream to zip file in 121s file size is 3.19MB * 10 void zipFile() { long begin = System.currentTimeMillis(); File file = new File(zipFile); try (ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(file))) { for (int i = 0; i < 10; i++) { System.out.println("compress at " + i + " time"); try (InputStream inputStream = new FileInputStream(JPG_FILE)) { zip.putNextEntry(new ZipEntry("FILE_NAME" + i)); int temp = 0; while ((temp = inputStream.read()) != -1) { zip.write(temp); } } } System.out.println("consumer time is " + (System.currentTimeMillis() - begin)); } catch (Exception e) { e.printStackTrace(); } }
使用了Bufferr 进行读取
Buffer 会比InputStream 快很多, 因为我们使用了缓冲区.
不会像之前的 read 那样一次只读一个Byte
//use Buffer to read to zip file in 2.5s file size is 3.19MB * 10 void bufferZipFile() { long begin = System.currentTimeMillis(); File file = new File(zipFile); try (ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(file)); BufferedOutputStream outputStream = new BufferedOutputStream(zip)) { for (int i = 0; i < 10; i++) { System.out.println("compress at " + i + " time"); try(BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(JPG_FILE))){ zip.putNextEntry(new ZipEntry("File_name" + i)); int temp = 0; while((temp = inputStream.read()) != -1){ outputStream.write(temp); } } } System.out.println("consumer time is " + (System.currentTimeMillis() - begin)); } catch (Exception e) { e.printStackTrace(); } }
使用了NIO 的channel 进行优化
channel 是使用了NIO 的操作系统模型, 会跟接近操作系统的习惯方式.,
//use Channel to read to zip file in 1.5s file size is 3.19MB * 10 void channelZipFile() { long begin = System.currentTimeMillis(); File file = new File(zipFile); try (ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(file)); WritableByteChannel channel = Channels.newChannel(zip)) { for (int i = 0 ; i < 10 ; i ++) { System.out.println("compress at " + i + " time"); try(FileChannel fileChannel = new FileInputStream(JPG_FILE).getChannel()){ zip.putNextEntry(new ZipEntry("File_name" + i)); fileChannel.transferTo(0, 3 * 1000 * 1000, channel); } } }catch (Exception e) { e.printStackTrace(); } System.out.println("consumer time is " + (System.currentTimeMillis() - begin)); }
总结:
为什么Buffer 缓存区 会比 慢慢读取要快很多呢?
因为这个就涉及到我们操作系统的 用户态和内核态了,
我们的应用程序都是属于用户态, 所以 当我们调用read 方法的时候 可以看到其实最后调用的是native 方法 read0.
/** * Reads a byte of data from this input stream. This method blocks * if no input is yet available. * * @return the next byte of data, or <code>-1</code> if the end of the * file is reached. * @exception IOException if an I/O error occurs. */ public int read() throws IOException { return read0(); } private native int read0() throws IOException;
而我们应用程序想要使用操作系统里面的操作, 就得使用操作系统对外的接口, 这样我们就可以 将我们的线程从 用户态 变到 内核态.
所以再使用inputstream.read 方法的时候, 时间会特别耗时.
然而当我们使用Buffer(缓冲区)的时候, 这个时候系统会自动给我们创建一个映射区, 这时候, 我们只用再映射区中找对应的文件就好.
然后为什么NIO 的channel 的速度又有了些提升呢?
* <p> This method does not modify this channel's position. If the given * position is greater than the file's current size then no bytes are * transferred. If the target channel has a position then bytes are * written starting at that position and then the position is incremented * by the number of bytes written. * * <p> This method is potentially much more efficient than a simple loop * that reads from this channel and writes to the target channel. Many * operating systems can transfer bytes directly from the filesystem cache * to the target channel without actually copying them. </p>
上面是channel transfer to 方法的注释, 上面说transferto 是直接写入到目标的channel 中, 而 没有copy 的过程 所以速度会更快一些.
用直接缓存区的缺点:
1. 不安全
2. 内存消耗增大 开辟的内存空间不由应用程序控制, 什么时候回收, 依赖于垃圾回收机.
3.控制, 当内容写入缓存区的时候, 应用程序就失去了对内容的控制, 这样子什么时候写入到文件中, 我们是没有办法进行控制的.