通常的NIO是指的NON Blocking IO, 非阻塞IO的缩写.

    java中的NIO, 官方释义是 New IO.  也有人说就是NON Blocking IO. 此处感觉无所谓.

    在java中的NIO编程与传统IO相比的区别是:

        传统IO是面向流的,而NIO是面向块的. 

    这块我得理解是, 都是渴了去接水, 传统IO是到水龙头那地儿张着嘴喝饱再走. 而NewIO是把水杯(Buffer缓冲区)放在水龙头下面,然后找个人(Selector)帮自己看着水有没有接满, 接满了告诉自己一声,收到哪个人的信号后NewIO再来拿杯子一饮而尽, 这样的好处就是接水的时间NewIO就可以忙自己的事情了.      此处如果没有Selector这个对象的话,NIO也是需要拿着杯子等着了,所以没有Selector的话也是阻塞的. 可以得出下面结论:

        在java中NIO编程可以是阻塞的, 也可以是非阻塞的.

    阻塞情况:  比如文件相关的File相关的Channel都是阻塞式的, 在读取文件时 只用到了NIO包下面的缓冲区 Buffer(一般用的是ByteBuffer,除了boolean类型的基础类型以及键值对等等都有相应的Buffer提供), 以及FileChannel. 线程走到这的时候依然是阻塞中的.  如果是套接字编程的话, 如果不引用Selector对象而只是用NIO包下面提供的ServerSocketChannel以及SocketChannel对象, 以及缓冲区对象 ByteBuffer的话其实线程也是阻塞的.

    非阻塞情况: 只有当在网路编程中(因为File相关的Channel不是实现的Selectable接口,也就没法用Selector去监听)才能实现非阻塞的编程. 这里需要注意,所有的在非阻塞式编程中用到的Channel都需要调用configureBlocking(false)方法来设置为非阻塞模式, 然后在Channel的register方法中注册Selector选择器的监听,这样就可以实现非阻塞的网络NIO编程了.

    另外NIO还有个特性就是能对缓冲区对象操作,具体的方法大概有以下几种:

    Buffer.flip()     重置当前读写游标position为0,同时将读写的结束位置limit放在游标重置之前的位置.(写入Channel(也就是读取Buffer)之前调用,这样读Buffer的时候就可以从头开始读到写入结束的位置了)

    Buffer.clear()  重置当前读写游标position为0,将读写结束位置limit放在缓冲区最后一位capacity处(重写Buffer之前调用,这样的游标再次写入会覆盖之前的数据,就当之前的数据不存在)

    Buffer.compact()  作用类似于clear()方法,但并不会重写之前的数据, 会把读写游标置于之前数据的limit之后,也就是用Buffer剩余空间做新的缓冲区, 适用于之前数据还需要用的情况,这时候可以配合mark()和reset()(?reset方法好像是,记不请了) 来读取之前的数据.

具体的Buffer使用可以参考这位大牛写的博客:  

下面是我从视频中临摹来大牛的一段NIO实现非阻塞套接字编程的代码:

public class TestNonBlockingNIO {
    //客户端
    @Test
    public void client() throws IOException {
        //1.获取通道
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));

        //2.切换成非阻塞模式
        sChannel.configureBlocking(false);

        //3.分配指定大小的缓冲区
        ByteBuffer buf=ByteBuffer.allocate(1024);

        //4.发送数据给服务端
        Scanner scan= new Scanner(System.in);

        while(scan.hasNext()){
            String str=scan.next();
            buf.put((new Date().toString()+"\n"+str).getBytes());
            buf.flip();
            sChannel.write(buf);
            buf.clear();
        }



        //5.关闭通道
        sChannel.close();
    }

    //服务端
    @Test
    public void server() throws IOException {
        //1.获取通道
        ServerSocketChannel ssChannel=ServerSocketChannel.open();

        //2.切换成非阻塞模式
        ssChannel.configureBlocking(false);

        //3.绑定连接
        ssChannel.bind(new InetSocketAddress(9898));

        //4.获取选择器
        Selector selector = Selector.open();

        ssChannel.register(selector, SelectionKey.OP_ACCEPT);

        //6.轮询式的获取选择器上已经 "准备就绪" 的事件
        while (selector.select()>0){

            //7.获取当前选择器中所有注册的"选择键(已就绪的监听事件)"
            Iterator<SelectionKey> it=selector.selectedKeys().iterator();

            while (it.hasNext()){
                //8.获取准备就绪的事件
                SelectionKey sk=it.next();
                //9.判断具体是什么时间准备就绪
                if(sk.isAcceptable()){
                    //10.若"接受就绪",获取客户端连接
                    SocketChannel sChannel = ssChannel.accept();

                    //11.切换非阻塞模式
                    sChannel.configureBlocking(false);

                    //12.将该通道注册到选择器上
                    sChannel.register(selector,SelectionKey.OP_READ);
                }else if(sk.isReadable()){
                    //13.获取当前选择器上"读就绪"状态的通道
                    SocketChannel sChannel=(SocketChannel)sk.channel();

                    //14.读取数据
                    ByteBuffer buf=ByteBuffer.allocate(1024);

                    int len=0;
                    while((len=sChannel.read(buf))>0){
                        buf.flip();
                        System.out.println(new String(buf.array(),0,len));
                        buf.clear();
                    }
                }
                //15.取消选择器
                it.remove();
            }
        }
    }
}