zoukankan      html  css  js  c++  java
  • channel和Stream的对比

    这篇文章主要想总结下NIO的channel的传统io中的stream的差别在哪。网上找了很多文章,都感觉只是说了概念。然后自己大概看了下源码,结合概念,整理一下。有些地方可能不是很准确,也希望可以给点意见,互相学习。

    这里不讲异步方面的东西,只是想单纯讲一下stream和channel在操作内存时的一些差异。

    让我产生问题的来源主要有俩个:

    1. 从概念上解读,stream是按照字节去处理的,看起来就像是水流一样,一个接一个。而channel是按照数据块来处理的。那么bufferedStream呢?加了buffer后是不是也是按照数据块来处理呢。那么这时,bufferedStream和channel的性能区别是在哪里呢?

    2. 网上发现了一些文章,通过拷贝文件的实验来对比channel和stream的性能。stream使用的是bufferedStream。结果是channel的性能要比stream快1/3。当然我没有做实验去验证,只是通过代码的解读来理解下为什么性能会有这么大的差别。

    首先需要引入一些背景知识,用户的线程是如何读入和写出文件的。下面这个图是从一本书上截下来的,简单的说明下读取文件时的流程。

    1. 磁盘的controller把数据从磁盘拷贝到系统内核区。

    2.然后cpu把数据从系统内核拷贝的用户的内存区。

    3.系统对内存块的操作是按数据块操作的,这也是NIO的一个重要的概念,操作数据时尽量和操作系统相吻合,来提高内存操作的效率。

     

    然后再分别分析一下channel和stream在操作内存时分别的步骤,就可以比较清晰的看出俩者的差别。

    一 channel:

    首先总结一下,这也是一个大家都知道的概念。channel的输入端和输出端都是byteBuffer。在内存操作的时候,也是以数据块为单位来进行数据移动。下面具体说下输出的步骤:

    1. 如果我们使用的是directBuffer。 那么会直接调用native方法,把整块的内存写出到磁盘。

    2.如果我们使用的是堆内的buffer,java会创建一个临时的directBuffer,把堆内buffer的数据拷贝到临时的 directBuffer。然后调用native方法把临时的 directBuffer的内容整块的写入磁盘。

    下面贴一下源码来看一下:

    static int write(FileDescriptor var0, ByteBuffer var1, long var2, NativeDispatcher var4) throws IOException {
            if(var1 instanceof DirectBuffer) {
                // 如果是directBuffer,直接整块写入磁盘
                return writeFromNativeBuffer(var0, var1, var2, var4);
            } else {
                int var5 = var1.position();
                int var6 = var1.limit();
    
                assert var5 <= var6;
    
                int var7 = var5 <= var6?var6 - var5:0;
                // 创建临时directBuffer
                ByteBuffer var8 = Util.getTemporaryDirectBuffer(var7);
    
                int var10;
                try {
                    var8.put(var1);
                    var8.flip();
                    var1.position(var5);
                    // 直接将临沭directBuffer整块写入磁盘
                    int var9 = writeFromNativeBuffer(var0, var8, var2, var4);
                    if(var9 > 0) {
                        var1.position(var5 + var9);
                    }
    
                    var10 = var9;
                } finally {
                    Util.offerFirstTemporaryDirectBuffer(var8);
                }
    
                return var10;
            }
        }

    二 stream

    首先也说一下概念,stream是按照字节,一个一个操作内存的。但是,真的是这样的吗?

    先看下FileOutputStream这个类:

        public void write(byte b[], int off, int len) throws IOException {
            writeBytes(b, off, len, append);
        }
    
        private native void writeBytes(byte b[], int off, int len, boolean append)
            throws IOException;

    可以看到,这里也是调用native方法,把整个byte块都写入了磁盘。具体native方法是怎么实现的,哪位大大可以帮忙分析一下?

    再看下BufferedOutPutStream这个类,主要看下write方法和flushBuffer方法

    public synchronized void write(byte b[], int off, int len) throws IOException {
    if (len >= buf.length) {
    /* If the request length exceeds the size of the output buffer,
    flush the output buffer and then write the data directly.
    In this way buffered streams will cascade harmlessly. */
    flushBuffer();
    out.write(b, off, len);
    return;
    }
    if (len > buf.length - count) {
    flushBuffer();
    }
    // 写入内部的buffer区
    System.arraycopy(b, off, buf, count, len);
    count += len;
    }

    /** Flush the internal buffer */ private void flushBuffer() throws IOException { if (count > 0) { 调用代理的outputStream的写出方法 out.write(buf, 0, count); count = 0; } }

    所以,从这个角度来看,stream和channel都是直接把整个数据块对底层进行写入的。

    那么,stream真的比channel慢吗? 值得怀疑。 哪位大佬可以帮忙解下惑呢?

  • 相关阅读:
    03_ if 练习 _ little2big
    uva 11275 3D Triangles
    uva 12296 Pieces and Discs
    uvalive 3218 Find the Border
    uvalive 2797 Monster Trap
    uvalive 4992 Jungle Outpost
    uva 2218 Triathlon
    uvalive 3890 Most Distant Point from the Sea
    uvalive 4728 Squares
    uva 10256 The Great Divide
  • 原文地址:https://www.cnblogs.com/zhaoxinshanwei/p/9636769.html
Copyright © 2011-2022 走看看