java中IO演變史

涉及到io主要涉及以下兩個方面:
1)同步和異步
2)阻塞和非阻塞

BIO

BIO ,全稱 Block-IO ,是一種阻塞 + 同步的通信模式。基於流(Stream)的模式。

每連接對應一個線程:線程數與客戶端連接數成1:1的關係,當併發大量來時,會導致系統線程被大量佔用,性能急劇下降。

NIO

NIO,Non-Block IO ,從 Java 1.4 版本開始引入的非阻塞 IO ,是一種非阻塞 + 同步的通信模式,也是 I/O 多路複用的基礎,基於緩衝區(Buffer)的模式。

NIO主要有三大核心

通過下圖簡單展示三大核心之間的關係:

Channel簡介

jdk中定義了一個java.nio.channels.channel接口,主要提供了開啓連接關閉連接的方法。

package java.nio.channels;

import java.io.IOException;
import java.io.Closeable;


/**
 * A nexus for I/O operations.
 *
 * <p> A channel represents an open connection to an entity such as a hardware
 * device, a file, a network socket, or a program component that is capable of
 * performing one or more distinct I/O operations, for example reading or
 * writing.
 *
 * <p> A channel is either open or closed.  A channel is open upon creation,
 * and once closed it remains closed.  Once a channel is closed, any attempt to
 * invoke an I/O operation upon it will cause a {@link ClosedChannelException}
 * to be thrown.  Whether or not a channel is open may be tested by invoking
 * its {@link #isOpen isOpen} method.
 *
 * <p> Channels are, in general, intended to be safe for multithreaded access
 * as described in the specifications of the interfaces and classes that extend
 * and implement this interface.
 *
 *
 * @author Mark Reinhold
 * @author JSR-51 Expert Group
 * @since 1.4
 */

public interface Channel extends Closeable {

    /**
     * Tells whether or not this channel is open.
     *
     * @return <tt>true</tt> if, and only if, this channel is open
     */
    public boolean isOpen();

    /**
     * Closes this channel.
     *
     * <p> After a channel is closed, any further attempt to invoke I/O
     * operations upon it will cause a {@link ClosedChannelException} to be
     * thrown.
     *
     * <p> If this channel is already closed then invoking this method has no
     * effect.
     *
     * <p> This method may be invoked at any time.  If some other thread has
     * already invoked it, however, then another invocation will block until
     * the first invocation is complete, after which it will return without
     * effect. </p>
     *
     * @throws  IOException  If an I/O error occurs
     */
    public void close() throws IOException;

在jdk中對於channel接口提供了很多實現類,最主要的是以下四個:
1)SocketChannel :一個客戶端用來發起 TCP 的 Channel 。
2)ServerSocketChannel :一個服務端用來監聽新進來的連接的 TCP 的 Channel 。對於每一個新進來的連接,都會創建一個對應的 SocketChannel 。
3)DatagramChannel :通過 UDP 讀寫數據。
4)FileChannel :從文件中,讀寫數據。

Buffer簡介

jdk提供java.nio.buffer抽象類,其屬性和構造如下代碼所示:

package java.nio;

import java.util.Spliterator;

public abstract class Buffer {

    /**
     * The characteristics of Spliterators that traverse and split elements
     * maintained in Buffers.
     */
    static final int SPLITERATOR_CHARACTERISTICS =
        Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;

    // Invariants: mark <= position <= limit <= capacity
    private int mark = -1;
    private int position = 0;
    private int limit;
    private int capacity;

    // Used only by direct buffers
    // NOTE: hoisted here for speed in JNI GetDirectBufferAddress
    long address;

    // Creates a new buffer with the given mark, position, limit, and capacity,
    // after checking invariants.
    //
    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;
        }
    }
}

如上代碼所示,Buffer有四個屬性,他們在Buffer讀和寫的情況下具有不同的含義

mark:記錄當前讀或寫的位置
position:下一個位置
limit:範圍
capacity:Buffer的容量,創建時候指定,不能修改。

寫模式下:

讀模式下:

主要方法:
每個buffer的實現類都實現了以下主要方法,下面以ByteBuffer的源碼舉例:
1)創建 Buffer:

/**
 * 創建並指定大小
 **/
public static ByteBuffer allocate(int capacity) {
        if (capacity < 0)
            throw new IllegalArgumentException();
        return new HeapByteBuffer(capacity, capacity);
    }

/**
 * 數組轉成Buffer對象
 **/
public static ByteBuffer wrap(byte[] array, int offset, int length)
    {
        try {
            return new HeapByteBuffer(array, offset, length);
        } catch (IllegalArgumentException x) {
            throw new IndexOutOfBoundsException();
        }
    }

2)寫入:

// 寫入 byte
public abstract ByteBuffer put(byte b); 
public abstract ByteBuffer put(int index, byte b);
// 寫入 byte 數組
public final ByteBuffer put(byte[] src) { ... }
public ByteBuffer put(byte[] src, int offset, int length) {...}

從channel將數據寫入buffer,該方法返回數據大小:

int num = channel.read(buffer);

3)讀取:

// 讀取 byte
public abstract byte get();
public abstract byte get(int index);
// 讀取 byte 數組
public ByteBuffer get(byte[] dst, int offset, int length) {...}
public ByteBuffer get(byte[] dst) {...}

從buffer中的數據寫入到buffer中,該方法返回數據大小:

int num = channel.write(buffer);
Selector簡介

Selector被稱爲多路複用器,用於輪詢NIO的channel是否處於可讀或者可寫的狀態,其位於jdk的java.nio.channels.Selector。

輪詢步驟:
1)每個channel需要註冊到selector上。
2)selector輪詢每個channel,當有channel發生讀寫操作,這個channel處於就緒狀態,會被輪詢到,等到就緒狀態的channel集合,進行後續的IO操作。

代碼舉例:

//創建selector
Selector selector = Selector.open();
//註冊channel
channel.configureBlocking(false); // 必須是非阻塞
/*
第二個參數有以下四種類型:
Connect :連接完成事件( TCP 連接 ),僅適用於客戶端,對應 SelectionKey.OP_CONNECT 
Accept:接受新連接事件,僅適用於服務端,對應 SelectionKey.OP_ACCEPT 。
Read :讀事件,適用於兩端,對應SelectionKey.OP_READ ,表示 Buffer 可讀。
Write :寫時間,適用於兩端,對應SelectionKey.OP_WRITE ,表示 Buffer 可寫。
*/
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);

AIO簡介

參考https://blog.csdn.net/qq_19642249/article/details/108383643

AIO是java中IO模型的一種,作爲NIO的改進和增強隨JDK1.7版本更新被集成在JDK的nio包中,因此AIO也被稱作是NIO2.0。AIO提供了從建立連接到讀、寫的全異步操作。AIO可用於異步的文件讀寫和網絡通信。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章