java-NIO-ByteBuffer

ByteBuf

同样是例子

    RandomAccessFile randFile = new RandomAccessFile(filename, "rw");
    FileChannel inChannel = randFile.getChannel();
    ByteBuffer buf = ByteBuffer.allocate(48);
    int bytesRead = inChannel.read(buf); //read into buffer.
    while (bytesRead != -1) {
        buf.flip();  //make buffer ready for read
        while(buf.hasRemaining()){
            System.out.print((char) buf.get()); // read 1 byte at a time
        }
        buf.clear(); //make buffer ready for writing
        bytesRead = inChannel.read(buf);
    }
    randFile.close();

用来存储byte类型的缓冲区,可以在这种缓冲区中存储任意其他基本类型的二进制值
此外还具有其他类型的buf

CharBuffer 
DoubleBuffer 
FloatBuffer 
IntBuffer 
LongBuffer
ShortBuffer

关于buf的特性,这个博客总结的不错http://www.cnblogs.com/youngKen/p/4923635.html
回顾例子代码

ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf);

ByteBuffer有两种,一种是堆内存中的,另一种是利用unsafe操作的
申请了

    public static ByteBuffer allocate(int capacity) {
        if (capacity < 0)
            throw new IllegalArgumentException();
        return new HeapByteBuffer(capacity, capacity);
    }

继承关系
HeapByteBuffer->ByteBuffer->Buffer
HeapByteBuffer

    HeapByteBuffer(int cap, int lim) {
        super(-1, 0, lim, cap, new byte[cap], 0);
    }

进而调用ByteBuffer的构造方法

    ByteBuffer(int mark, int pos, int lim, int cap,   // package-private
                 byte[] hb, int offset) {
        super(mark, pos, lim, cap);
        this.hb = hb;
        this.offset = offset;
    }

继续构造父类Buffer

    Buffer(int mark, int pos, int lim, int cap) {       // package-private
        if (cap < 0)
            throw new IllegalArgumentException("Negative capacity: " + cap);
        this.capacity = cap;
        limit(lim);
        position(pos);
        if (mark >= 0) {
            if (mark > pos)
                throw new IllegalArgumentException("mark > position: ("
                                                   + mark + " > " + pos + ")");
            this.mark = mark;
        }
    }

可以看出ByteBuffer是在Buffer的基础上增加了hb和offset,hb是创建的byte数组

FileChannel的read方法
FileChannelImpl中有个positionLock,首先锁住位置
回到例子中的inChannel.read(buf)把read传入

    public int read(ByteBuffer dst) throws IOException {
		//验证channel是否打开
        ensureOpen();
        if (!readable)
            throw new NonReadableChannelException();
        synchronized (positionLock) {
            int n = 0;
            int ti = -1;
            try {
                begin();
                ti = threads.add();
                if (!isOpen())
                    return 0;
                do {
                    n = IOUtil.read(fd, dst, -1, nd);
                } while ((n == IOStatus.INTERRUPTED) && isOpen());
                return IOStatus.normalize(n);
            } finally {
                threads.remove(ti);
                end(n > 0);
                assert IOStatus.check(n);
            }
        }
    }

begin是父类的方法,为channel设置一个interruptor

    protected final void begin() {
        if (interruptor == null) {
            interruptor = new Interruptible() {
                    public void interrupt(Thread target) {
                        synchronized (closeLock) {
                            if (!open)
                                return;
                            open = false;
                            interrupted = target;
                            try {
                                AbstractInterruptibleChannel.this.implCloseChannel();
                            } catch (IOException x) { }
                        }
                    }};
        }
        blockedOn(interruptor);
        Thread me = Thread.currentThread();
        if (me.isInterrupted())
            interruptor.interrupt(me);
    }

implCloseChannel调子类的方法,执行关闭操作

   protected void implCloseChannel() throws IOException {
        if (fileLockTable != null) {
            for (FileLock fl: fileLockTable.removeAll()) {
                synchronized (fl) {
                    if (fl.isValid()) {
                        nd.release(fd, fl.position(), fl.size());
                        ((FileLockImpl)fl).invalidate();
                    }
                }
            }
        }

        nd.preClose(fd);
        threads.signalAndWait();

        if (parent != null) {
            ((java.io.Closeable)parent).close();
        } else {
            nd.close(fd);
        }

    }

NativeThreadSet的add方法
在集合中添加native线程

    int add() {
        long th = NativeThread.current();
        // 0 and -1 are treated as placeholders, not real thread handles
        if (th == 0)
            th = -1;
        synchronized (this) {
            int start = 0;
            if (used >= elts.length) {
                int on = elts.length;
                int nn = on * 2;
                long[] nelts = new long[nn];
                System.arraycopy(elts, 0, nelts, 0, on);
                elts = nelts;
                start = on;
            }
            for (int i = start; i < elts.length; i++) {
                if (elts[i] == 0) {
                    elts[i] = th;
                    used++;
                    return i;
                }
            }
            assert false;
            return -1;
        }
    }

二. 读取方法

读取方法是,把文件描述符关联的文件内容拷贝到临时buf中,然后把临时buf内容拷贝到目标buf中

	do {
	    n = IOUtil.read(fd, dst, -1, nd);
	} while ((n == IOStatus.INTERRUPTED) && isOpen());

IOUtil.read方法中,先生成一个临时的DirectByteBuffer,把文件中的内容读取到这个临时buf中,最后把临时buf设置为只读状态,然后写入到目标buf中

    static int read(FileDescriptor fd, ByteBuffer dst, long position,
                    NativeDispatcher nd)
        throws IOException {
        if (dst.isReadOnly())
            throw new IllegalArgumentException("Read-only buffer");
        if (dst instanceof DirectBuffer)
            return readIntoNativeBuffer(fd, dst, position, nd);

      	//申请一个临时的buffer
        ByteBuffer bb = Util.getTemporaryDirectBuffer(dst.remaining());
        try {
            int n = readIntoNativeBuffer(fd, bb, position, nd);
            bb.flip();
            if (n > 0)
                dst.put(bb);
            return n;
        } finally {
            Util.offerFirstTemporaryDirectBuffer(bb);
        }
    }

dst.put(bb);的过程如下,就是把bb的内容拷贝到接收buf中,然后位置指针都向前移动

    HeapByteBuffer sb = (HeapByteBuffer)src;
	//limit-position还能够读取的量
    int n = sb.remaining();	
	//如果写入的内容比可以使用的多,那么溢出
    if (n > remaining()) throw new BufferOverflowException();
    System.arraycopy(sb.hb, sb.ix(sb.position()), hb, ix(position()), n);
    sb.position(sb.position() + n);
    position(position() + n);

三. 写入方法

	do {
	    n = IOUtil.write(fd, src, -1, nd);
	} while ((n == IOStatus.INTERRUPTED) && isOpen());

不断调用IOUtil的写方法,获取ByteBuffer的pos,lim等

    static int write(FileDescriptor fd, ByteBuffer src, long position,
                     NativeDispatcher nd)
        throws IOException {
        if (src instanceof DirectBuffer)
            return writeFromNativeBuffer(fd, src, position, nd);
        int pos = src.position();
        int lim = src.limit();
        assert (pos <= lim);
        int rem = (pos <= lim ? lim - pos : 0);
        ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
        try {
			//把原始内容写入到临时buf中
            bb.put(src);
			//临时buf变为读状态
            bb.flip();
            // 更新positiojn
            src.position(pos);
			//临时buf内容写到文件中
            int n = writeFromNativeBuffer(fd, bb, position, nd);
            if (n > 0) {
                // now update src
                src.position(pos + n);
            }
            return n;
        } finally {
            Util.offerFirstTemporaryDirectBuffer(bb);
        }
    }

getTemporaryDirectBuffer返回一个临时的buffer,这里的bufferCache是一个ThreadLocal,内部存储的BufferCache对象,BufferCache是Util的内部类
这个BufferCache是ByteBuffer的缓存,具体实现可以参见sun.nio.ch.Util的这个内部类

    public static ByteBuffer getTemporaryDirectBuffer(int size) {
        BufferCache cache = bufferCache.get();
        ByteBuffer buf = cache.get(size);	//获取一个临时的ByteBuffer,获取至少这么大的
        if (buf != null) {
            return buf;
        } else {
            if (!cache.isEmpty()) {
                buf = cache.removeFirst();
                free(buf);
            }
            return ByteBuffer.allocateDirect(size);
        }
    }

四. 其他

    public final boolean hasRemaining() {
        return position < limit;
    }

buf.get()方法只读取一个内容,然后递增index

    public byte get() {
        return hb[ix(nextGetIndex())];
    }
posted @ 2016-09-30 16:08  zhangshihai1232  阅读(222)  评论(0)    收藏  举报