1. 程式人生 > >JAVA NIO 之1

JAVA NIO 之1

第一次接觸,所以先從定義開始吧

java.nio  全程是java non-blocking IO,非阻塞型IO。根據這個段時間的學習,知道,java.IO是同步阻塞型的,每一次呼叫writer和read的時候,都會加鎖,意味如果很多使用者的話,必須排隊

NIO主要有三大核心部分:Channel,Buffer,Selector。傳統IO基於位元組流和字元流進行操作,而NIO基於Channel(通道)和Buffer(緩衝區)進行操作。資料總是由通道讀取到緩衝區,或者從緩衝區寫入到通道。Selector(選擇區)用於監聽多個通道的事件(比如:連線開啟,資料到達)。因此單執行緒可以監聽多個數據通道。

NIO和傳統IO的第一個最大的區別是,IO是面向流的,NIO是面向緩衝區的。

IO的各種流是阻塞的。這意味著,當一個執行緒呼叫read或write時,該執行緒被阻塞,直到有一些 資料被讀取,或資料完全寫入。該執行緒在此期間不能幹任何事情。NIO是非阻塞模式,使一個執行緒從某通道傳送請求讀取資料,但它只能得到目前可用的資料。如果目前沒有資料可用時,就什麼也不會獲取。而不是保持執行緒阻塞,所以直到資料變的可讀取之前,該執行緒可以繼續做其他的事情,非阻塞的寫也是如此。執行緒通常將非阻塞IO空閒時間用在其他通道上執行IO操作,所以一個單獨的執行緒在現在可以管理多個輸入和輸出通道(channel)。

channel,不像流,流是單向的,channel是雙向的,既可以用來讀,也可以用來寫。

//通道表示一個實體的(硬體裝置,檔案,網路套接字,一個程式元件)的開放性連線,執行一個多或多個IO操作。讀或寫。。。
//通道要麼開啟,要麼關閉,通道在建立的時候開啟,關閉的時候關閉。
//一旦關閉。任何關於IO的操作都會拋異常
//多執行緒訪問安全
public interface Channel extends Closeable{
    //判斷通道是否開啟
    public boolean isOpen();
    //關閉通道
    public void close() throws IOException;
}


buffer,buffer用於和channel進行互動,資料是從通道進入緩衝區,從緩衝區寫入到通道中的 。

    實際上是一個容器,連續陣列,Channel提供從檔案、網路讀取資料的渠道,但是讀寫的資料都必須經過Buffer

    一個buffer的capacity是buffer包含元素的數量,buffer的capacity是不為負且不會改變

    buffer的limit是第一個不被讀或寫的元素的指標,buffer的limit不為負且不能超過buffer的capacity

    buffer的position是下一個將要被讀或寫的元素的指標,buffer的position不為負且不能超過buffer的limit

private int mark = -1; //用於記錄當前position的前一個位置或者預設是-1
private int position = 0;
private int limit;
private int capacity;

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;
        }
    }

public final Buffer limit(int newLimit) {
        if ((newLimit > capacity) || (newLimit < 0))
            throw new IllegalArgumentException();
        limit = newLimit;
        if (position > limit) position = limit;
        if (mark > limit) mark = -1;
        return this;
    }

public final Buffer position(int newPosition) {
        if ((newPosition > limit) || (newPosition < 0))
            throw new IllegalArgumentException();
        position = newPosition;
        if (mark > position) mark = -1;
        return this;
    }

 向Buffer中寫的資料

     從Channel中寫入。。。

selector:selector執行單執行緒處理多個Channel。

關於FileChannel

//   是一個讀取,寫入,對映,和操作檔案的通道
//   java.io.FileInputStream#getChannel()
//   java.io.FileOutputStream#getChannel()
//   java.io.RandomAccessFile#getChannel()

public abstract class FileChannel
    extends AbstractInterruptibleChannel
    implements SeekableByteChannel, GatheringByteChannel, ScatteringByteChannel
//初始化一個新的例項 
protected FileChannel() { }

//開啟或建立一個檔案,返回訪問檔案的通道
//path 檔案的路徑
//options 檔案開啟的方式
//attrs 可選的檔案屬性列表
public static FileChannel open(Path path,
                                   Set<? extends OpenOption> options,
                                   FileAttribute<?>... attrs)
        throws IOException
    {
        FileSystemProvider provider = path.getFileSystem().provider();
        return provider.newFileChannel(path, options, attrs);
    }

//從通道中讀取一個位元組序列到ByteBuffer中
/每寫讀次,ByteBuffer 中的positon都會更新,大小為positon + n
//其中 n  = ByteBuffer.limit,若最後一次,就是最後一次讀取的byte的數目
public abstract int read(ByteBuffer dst) throws IOException;

//從通道中讀取位元組序列到指定的ByteBuffer陣列
public abstract long read(ByteBuffer[] dsts, int offset, int length)
        throws IOException;

public final long read(ByteBuffer[] dsts) throws IOException {
        return read(dsts, 0, dsts.length);
    }
//Bytes 從該通道的當前檔案位置開始讀取位元組,然後使用實際讀取的位元組數更新檔案位置。

//從給定的buffer中,向通道中寫一序列位元組
//每寫一次,ByteBuffer 中的positon都會更新,大小為positon + n
//其中 n  = ByteBuffer.limit,若最後一次,就是最後一次讀取的byte的數目
public abstract int write(ByteBuffer src) throws IOException;

//從給定的buffer[]中,從offset開始,讀取length位元組,向通道中寫入
 public abstract long write(ByteBuffer[] srcs, int offset, int length)
        throws IOException;

 public final long write(ByteBuffer[] srcs) throws IOException {
        return write(srcs, 0, srcs.length);
 }

//返回通道中檔案的位置
public abstract long position() throws IOException;
//設定通道中檔案的位置
public abstract FileChannel position(long newPosition) throws IOException;
//返回當前通道中檔案的大小(實時)
public abstract long size() throws IOException;
//將此通道的檔案擷取為指定大小
public abstract FileChannel truncate(long size) throws IOException;
//將位元組從此通道的檔案傳輸到給定的可寫位元組通道。
public abstract long transferTo(long position, long count,
                                    WritableByteChannel target)
        throws IOException;
。。。。。。。。。。。。。
public class JavaNio {
    public static void main(String[] args) throws IOException {
        InputStream in = new FileInputStream("E:\\sourcecode\\hello.txt");
        FileChannel inChannel = ((FileInputStream) in).getChannel();
        OutputStream out = new FileOutputStream("E:\\sourcecode\\hello1.txt");
        FileChannel outChannel = ((FileOutputStream) out).getChannel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(2);
        while((inChannel.read(byteBuffer))!=-1){
            byteBuffer.flip();
            if(byteBuffer.hasRemaining()){
                outChannel.write(byteBuffer);
            }
            byteBuffer.compact();
        }
        out.close();
        in.close();
    }
}

//分配一個新的位元組緩衝區。
public static ByteBuffer allocate(int capacity) {
        if (capacity < 0)
            throw new IllegalArgumentException();
        return new HeapByteBuffer(capacity, capacity);
    }

HeapByteBuffer(int cap, int lim) {            // package-private

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

/***************************************************
buf.clear();          // Prepare buffer for use
while (in.read(buf) >= 0 || buf.position != 0) {
    buf.flip();
    out.write(buf);
    buf.compact();    // In case of partial write
}
***************************************************/

public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

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