1. 程式人生 > >Java NIO總結

Java NIO總結

address 操作系統 exce exceptio bool 有一個 fileinput throw 基本數據類型

一、NIO

NIO是new IO,也是非阻塞IO。有Channel、Selector、Buffer、Pipe、FileLock等類。

Buffer在java.nio包

Channel、Selector、Pipe、FileLock等在java.nio.channels包

二、Channel通道

設置非阻塞configureBlocking(false);

註冊選擇器register(selector,SelectionKey.OP_XXX)

使用方法read(Buffer) ,write(Buffer)

使用方法open()獲取Channel

  • FileChannel 使用FileInputStream,FileOutputStream,RandomAccessFile或者open(Path path, OpenOption... options)可以獲取channel對象
  • ServerSocketChannel
  • SocketChannel
  • DatagramChannel
  • Pipe.SinkChannel 使用pipe.sink()
  • Pipe.SourceChannel 使用pipe.source()

三、Buffer緩沖

除了boolean類型沒有Buffer類,其他七種基本數據類型都有Buffer緩沖。而byte有兩個Buffer緩沖,一個是在堆,一個在文件的內存映射區.

方法clear() 清空Buffer

方法compact()移動Buffer數據到起始位置,設置為position在數據末尾

方法flip() 反轉Buffer,將寫模式轉成讀模式

方法rewind() 從0開始讀數據

方法hasRemaining()判斷是否還有剩余數據

ByteBuffer

ShortBuffer

IntBuffer

LongBuffer

FloatBuffer

DoubleBuffer

CharBuffer

MappedByteBuffer 通過FileChannel.map方法產生

四、Selector 選擇器

Selector selector = Selector.open();
channel.configureBlocking(false);
SelectionKey key = channel.register(selector,Selectionkey.OP_READ);

select方法

int select()
int select(long timeout)
int selectNow()

select()阻塞到至少有一個通道在你註冊的事件上就緒了。

select(long timeout)和select()一樣,除了最長會阻塞timeout毫秒(參數)。

selectNow()不會阻塞,不管什麽通道就緒都立刻返回

調用Selector.wakeup()方法,阻塞在select()方法上的線程會立馬返回。

用完Selector後調用其close()方法會關閉該Selector,且使註冊到該Selector上的所有SelectionKey實例無效。通道本身並不會關閉。

五、SelectionKey

Selector四種事件用SelectionKey的四個常量來表示:

SelectionKey.OP_CONNECT

SelectionKey.OP_ACCEPT

SelectionKey.OP_READ

SelectionKey.OP_WRITE

在Selector的select方法返回不為0時,獲取SelectionKey

Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
SelectionKey key
= keyIterator.next(); if(key.isAcceptable()) { // a connection was accepted by a ServerSocketChannel. } else if (key.isConnectable()) { // a connection was established with a remote server. } else if (key.isReadable()) { // a channel is ready for reading } else if (key.isWritable()) { // a channel is ready for writing } keyIterator.remove(); //必須移除掉,否則下一次channel還留在selectionkeySet中。 }

六、Pipe 管道

(1)SinkChannel

Pipe pipe = Pipe.open();
Pipe.SinkChannel sinkChannel = pipe.sink();
String data = "a sink pipe channel";
ByteBuffer buf = ByteBuffer.allocate(50);
buf.clear();
buf.put(data.getBytes());
buf.flip();

while(buf.hasRemaining()) {
    sinkChannel.write(buf);
}

(2)SourceChannel

Pipe.SourceChannel sourceChannel = pipe.source();
ByteBuffer buf = ByteBuffer.allocate(50);
int length = sourceChannel.read(buf);

七、FileLock

FileLock與Lock接口相似,但沒有實現Lock接口

(1)概念

  • 共享鎖: 共享讀操作,但只能一個寫(讀可以同時,但寫不能)
  • 獨占鎖: 只有一個讀或一個寫(讀和寫都不能同時)

(2)FileLock FileChannel.lock(long position, long size, boolean shared)

shared的含義:是否使用共享鎖,一些不支持共享鎖的操作系統,將自動將共享鎖改成排它鎖。可以通過調用isShared()方法來檢測獲得的是什麽類型的鎖。

(3) lock()和tryLock()的區別:

lock()阻塞的方法,鎖定範圍可以隨著文件的增大而增加。無參lock()默認為獨占鎖;有參lock(0L, Long.MAX_VALUE, true)為共享鎖。

tryLock()非阻塞,當未獲得鎖時,返回null.

(4)FileLock的生命周期:在調用FileLock.release(),或者Channel.close(),或者JVM關閉

(5)FileLock是線程安全的

(6)同一進程內,在文件鎖沒有被釋放之前,不可以再次獲取。即在release()方法調用前,只能lock()或者tryLock()一次。

        FileChannel channel = null;
        FileLock lock=null;
        try {
            RandomAccessFile raf = new RandomAccessFile("data.txt", "rw");
            channel = raf.getChannel();
            //獲得鎖方法一:lock(),阻塞的方法,當文件鎖不可用時,當前進程會被掛起  
            lock = channel.lock();//無參lock()為獨占鎖  
            //lock = channel.lock(0L, Long.MAX_VALUE, true);//有參lock()為共享鎖,有寫操作會報異常  

            //獲得鎖方法二:trylock(),非阻塞的方法,當文件鎖不可用時,tryLock()會得到null值  
            //do {  
            //  lock = channel.tryLock();  
            //} while (null == lock); 
        } catch (Exception e) {
        }finally{
            if (lock!=null) {
                lock.release();
            }
            if (channel!=null) {
                channel.close();
            }
        }

八、ServerSocketChannel 服務器通道

package cn.edu.scau.mk;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

/**
 *
 * @author MK
 */
public class Test {

    volatile static boolean isFinished = false;

    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(888));
        while (!isFinished) {
            SocketChannel socketChannel = serverSocketChannel.accept();
            //1.阻塞模式
            //線程處理socketChannel...
            //2.非阻塞模式
            if (socketChannel != null) {
                //處理socketChannel...
            }

        }
        serverSocketChannel.close();

    }
}

九、SocketChannel TCP通道

package cn.edu.scau.mk;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

/**
 *
 * @author MK
 */
public class Test {

    volatile static boolean isFinished = false;

    public static void main(String[] args) throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false);//設置為非阻塞
        socketChannel.connect(new InetSocketAddress("https://www.baidu.com", 80));
        String data = "a socket channel connect";
        ByteBuffer buf = ByteBuffer.allocate(48);
        buf.clear();
        buf.put(data.getBytes());
        buf.flip();
        while (!socketChannel.finishConnect()) {
            //等待連接成功或者做其他的事
        }
        //非阻塞狀態有可能沒有寫入數據就返回了
        while (buf.hasRemaining()) {
            socketChannel.write(buf);
        }
        socketChannel.close();
    }
}

十、DatagramChannel UDP通道

package cn.edu.scau.mk;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SocketChannel;

/**
 *
 * @author MK
 */
public class Test {

    volatile static boolean isFinished = false;

    public static void main(String[] args) throws IOException {
        DatagramChannel channel = DatagramChannel.open();
        channel.socket().bind(new InetSocketAddress(888));
        //channel.connect(new InetSocketAddress("www.baidu.com", 80));
        ByteBuffer buf = ByteBuffer.allocate(48);
        buf.clear();
        //接收
        SocketAddress sa=channel.receive(buf);
        //connect通道可以使用read
        //int bytesRead = channel.read(buf);

        buf.clear();
        buf.put("datagram channel".getBytes());
        buf.flip();
        //發送
        int length = channel.send(buf, new InetSocketAddress("www.baidu.com", 80));
        //connect通道可以使用write
        //channel.write(buf);
        channel.close();
    }
}

Java NIO總結