利不百不变法,功不十不易器

为什么会出现nio,之前的io有什么问题?

请先看 说说nio1

nio类图例如以下

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGxmMTIzMzIx/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

这里面多了几个类,Channel,Selector,Buffer;

我们能够这样理解,Channel就是在装载乘客的交通工具(它的详细形式,FileChannel,ServerSocketChannel就是公共汽车或者火车或者飞机)

Selector就是交通管理系统,它负责管理车辆的当前执行状态,是已经出站,还是在路上等等#

Buffer能够理解为交通工具上的座位#



这里对他们最简单的使用,我举think in java上的一个样例

package io;
//: io/GetChannel.java
// Getting channels from streams
import java.nio.*;
import java.nio.channels.*;
import java.io.*;

public class GetChannel {
  private static final int BSIZE = 1024;
  public static void main(String[] args) throws Exception {
    // Write a file:
    FileChannel fc =
      new FileOutputStream("data.txt").getChannel();
    fc.write(ByteBuffer.wrap("Some text ".getBytes()));
    fc.close();
    // Add to the end of the file:
    fc =
      new RandomAccessFile("data.txt", "rw").getChannel();
    fc.position(fc.size()); // Move to the end
    fc.write(ByteBuffer.wrap("Some more".getBytes()));
    fc.close();
    // Read the file:
    fc = new FileInputStream("data.txt").getChannel();
    ByteBuffer buff = ByteBuffer.allocate(BSIZE);
    fc.read(buff);
    buff.flip();
    while(buff.hasRemaining())
      System.out.print((char)buff.get());
  }
}
/* Output:
Some text Some more
*///:~

另外我想声明一下,在eclipse里面, FileChannel fc =new FileOutputStream("data.txt").getChannel()这个data.txt就应该在项目的根路径下,不再src或bin里面!!

关于nio转换数据,获取基本类型,试图缓冲器,内存映射文件的概念大家能够查阅think in java,这里仅仅说最简单的概念#

在上文我们还提到了关于多个线程,载入同一个问题的问题#如今我们说说nio里面锁的概念

不多说了,先看一段代码

package io;

import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.concurrent.*;
import java.io.*;

public class FileLocking {
  public static void main(String[] args) throws Exception {
    FileOutputStream fos= new FileOutputStream("file.txt");
    FileLock fl = fos.getChannel().tryLock();
    if(fl != null) {
      System.out.println("Locked File");
      FileChannel fc =fos.getChannel();
      fc.write(ByteBuffer.wrap("Some textssss".getBytes()));

      TimeUnit.MILLISECONDS.sleep(1000);
      fl.release();
      System.out.println("Released Lock");
    }
    fos.close();
  }
} /* Output:
Locked File
Released Lock
*///:~

这里我想说说关于锁的获取,有两种方式,

调用FileChannel的lock方法或tryLock方法;

tryLock是非堵塞的,假设对于的文件已经被加锁,他就直接返回

lock是堵塞式的,它会一直堵塞直到锁能够获得;

另外锁包括独占锁与共享锁,详细信息可查看其他资料#

如今看一个复杂些的样例

package io;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;

/**
 * 測试NIO中的文件锁:三个线程争抢文件锁。获得锁后向文件里写数据,然后再释放文件锁。

*
 * @author aofeng <a href="mailto:aofengblog@163.com>aofengblog@163.com</a>
 */
public class LockTest implements Runnable {
    public void run() {
        Thread curr = Thread.currentThread();
        System.out.println("Current executing thread is " + curr.getName());

        URL url = LockTest.class.getClassLoader().getResource("file.txt");
        //路径问题
        //http://www.cnblogs.com/rongxh7/archive/2010/04/22/1718178.html
        //http://www.cnblogs.com/yejg1212/p/3270152.html
        RandomAccessFile raf = null;
        FileChannel fc = null;
        FileLock lock = null;
        try {
                        //就是这里的路径问题, 为什么要替换%20 去上面的资料里看
            raf = new RandomAccessFile(url.getPath().replaceAll("%20"," "), "rw");
            fc = raf.getChannel();
            System.out.println(curr.getName() + " ready");

            // 轮流获得文件独占锁。
            while (true) {
                try {
                    lock = fc.lock();
                    break;
                } catch (OverlappingFileLockException e) {
                    Thread.sleep(1 * 1000);
                }
            }

            if (null != lock) {
                System.out.println(curr.getName() + " get filelock success");
                fc.position(fc.size());
                fc.write(ByteBuffer.wrap((curr.getName() + " write data. ")
                        .getBytes()));
            } else {
                System.out.println(curr.getName() + " get filelock fail");
            }
            fc.close();
            raf.close();
            } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 注意:要先释放锁,再关闭通道。
            if (null != lock && lock.isValid()) {
                try {
                    lock.release();
                    System.out.println(curr.getName() + " release filelock");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        Thread t1 = new Thread(new LockTest());
        t1.setName("t1");
        Thread t2 = new Thread(new LockTest());
        t2.setName("t2");
        Thread t3 = new Thread(new LockTest());
        t3.setName("t3");

        t1.start();
        t2.start();
        t3.start();
    }
}

结果例如以下

Current executing thread is t1

Current executing thread is t2

Current executing thread is t3

t3 ready

t3 get filelock success

t1 ready

t1 get filelock success

t2 ready

t2 get filelock success

注意这是三个线程争用对文档的读写权利,因此读写的顺序,每次执行的结果不一定一样#

ok如今我们看看在网络中,nio是在怎么运作的

非堵塞 IO 的支持能够算是 NIO API 中最重要的功能。非堵塞 IO 同意应用程序同一时候监控多个 channel 以提高性能。这一功能是通过 Selector , SelectableChannel 和SelectionKey 这 3 个类来实现的。

SelectableChannel 代表了能够支持非堵塞 IO 操作的 channel ,能够将其注冊在 Selector 上,这样的注冊的关系由 SelectionKey 这个类来表现(见 UML 图)。 在Selector中可通过selectedKeys方法获得key集合

Selector 这个类通过 select() 函数,给应用程序提供了一个能够同一时候监控多个 IO channel 的方法:



 



应用程序通过调用 select() 函数,让 Selector 监控注冊在其上的多个 SelectableChannel ,当有 channel 的 IO 操作能够进行时, select() 方法就会返回以让应用程序检查 channel 的状态。并作对应的处理。

public static void acceptConnections( int port) throws Exception {

      // 打开一个 Selector
      Selector acceptSelector = SelectorProvider.provider().openSelector();

      // 创建一个 ServerSocketChannel 。这是一个 SelectableChannel 的子类
      ServerSocketChannel ssc = ServerSocketChannel.open();

      // 将其设为 non-blocking 状态,这样才干进行非堵塞 IO 操作
      ssc.configureBlocking( false );

      // 给 ServerSocketChannel 相应的 socket 绑定 IP 和端口
      InetAddress lh = InetAddress.getLocalHost();
      InetSocketAddress isa = new InetSocketAddress(lh, port);
      ssc.socket().bind(isa);

      // 将 ServerSocketChannel 注冊到 Selector 上,返回相应的 SelectionKey
      ssc.register(acceptSelector, SelectionKey.OP_ACCEPT);

      int keysAdded = 0;
      // 用 select() 函数来监控注冊在 Selector 上的 SelectableChannel
      // 返回值代表了有多少 channel 能够进行 IO 操作 (ready for IO)

      while ((keysAdded = acceptSelector.select()) > 0) {
          // selectedKeys() 返回一个 SelectionKey 的集合,
          // 当中每一个 SelectionKey 代表了一个能够进行 IO 操作的 channel 。

// 一个 ServerSocketChannel 能够进行 IO 操作意味着有新的 TCP 连接连入了
          Set<SelectionKey> readyKeys = acceptSelector.selectedKeys();
          Iterator<SelectionKey> i = readyKeys.iterator();

          while (i.hasNext()) {
             SelectionKey sk = (SelectionKey) i.next();
             // 须要将处理过的 key 从 selectedKeys 这个集合中删除

             i.remove();
             // 从 SelectionKey 得到相应的 channel
             ServerSocketChannel nextReady =(ServerSocketChannel) sk.channel();
             // 接受新的 TCP 连接

             Socket s = nextReady.accept().socket();
             // 把当前的时间写到这个新的 TCP 连接中

             PrintWriter out =new PrintWriter(s.getOutputStream(), true );

             Date now = new Date();
             out.println(now);
             // 关闭连接
             out.close();
          }

      }

   }

一般来说 I/O 模型能够分为:同步堵塞,同步非堵塞,异步堵塞,异步非堵塞 四种IO模型

同步堵塞 IO :  我们在上一篇文章nio1里面说的就是这个

在此种方式下,用户进程在发起一个 IO 操作以后,必须等待 IO 操作的完毕,仅仅有当真正完毕了 IO 操作以后,用户进程才干执行。

JAVA传统的 IO 模型属于此种方式!

我去找小明借书 小明開始在自己的家里找 我什么事情都不干 就在他家里等 他找到后给我 这就是同步堵塞

同步非堵塞 IO:  这篇文章说nio就是 指这个

在此种方式下,用户进程发起一个 IO 操作以后 边可 返回做其他事情,可是用户进程须要时不时的询问 IO 操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的 CPU 资源浪费。当中眼下 JAVA 的 NIO 就属于同步非堵塞 IO 。

我去找小明借书 小明開始找 我先去玩 过一会来看 假设没有找到 那就继续去玩 等会再来看 找到了 那就一切OK

异步堵塞 IO : 还没有提到

此种方式下是指应用发起一个 IO 操作以后,不等待内核 IO 操作的完毕,等内核完毕 IO 操作以后会通知应用程序,这事实上就是同步和异步最关键的差别,同步必须等待或者主动的去询问 IO 是否完毕,那么为什么说是堵塞的呢?由于此时是通过 select 系统调用来完毕的,而 select 函数本身的实现方式是堵塞的,而採用 select 函数有个优点就是它能够同一时候监听多个文件句柄。从而提高系统的并发性!

我去找小明借书 小明開始找 我先去玩 他找到书后 会给我送来



异步非堵塞 IO:

在此种模式下,用户进程仅仅须要发起一个 IO 操作然后马上返回,等 IO 操作真正的完毕以后,应用程序会得到 IO 操作完毕的通知,此时用户进程仅仅须要对数据进行处理就好了,不须要进行实际的 IO 读写操作。由于 真正的 IO读取或者写入操作已经由 内核完毕了。眼下 Java 中还没有支持此种 IO 模型。

关于异步非堵塞 IO:  请參考 http://janeky.iteye.com/blog/1073695

參考资料

http://www.cnblogs.com/zhuYears/archive/2012/09/28/2690194.html

http://aofengblog.blog.163.com/blog/static/631702120089276182626/

Think in java 第四版 第18章i/o系统

http://www.blogjava.net/19851985lili/articles/93524.html

http://www.zhihu.com/question/27991975/answer/69041973

深入分析java web内幕 许令波 第二章 深入分析java/io的工作机制

http://janeky.iteye.com/blog/1073695

说说nio2的更多相关文章

  1. Java新AIO/NIO2:AsynchronousServerSocketChannel和AsynchronousSocketChannel简单服务器-客户端

    Java新AIO/NIO2:AsynchronousServerSocketChannel和AsynchronousSocketChannel简单服务器-客户端用AsynchronousServerS ...

  2. BIO,NIO,AIO(NIO2)的理解

    写在前面,这里所说的IO主要是强调的网络IO 1.BIO(同步并阻塞) 客户端一个请求对应一个线程.客户端上来一个请求(最开始的连接以及后续的IO请求),服务端新建一个线程去处理这个请求,由于线程总数 ...

  3. java7 NIO2 watching service API

    java7 NIO2新增了文件系统的相关事件处理API,为目录,文件新增修改删除等事件添加事件处理. package reyo.sdk.utils.file; import java.io.IOExc ...

  4. Java基础——NIO(二)非阻塞式网络通信与NIO2新增类库

    一.NIO非阻塞式网络通信 1.阻塞与非阻塞的概念  传统的 IO 流都是阻塞式的.也就是说,当一个线程调用 read() 或 write() 时,该线程被阻塞,直到有一些数据被读取或写入,该线程在 ...

  5. Java IO------------------BIO(同步阻塞)、NIO1.0(多路复用)、NIO2.0(AIO,非阻塞)

    1. BIO JDK5之前, JDK的IO模式只有BIO(同步阻塞)问题: 因为阻塞的存在, 需对每个请求开启一个线程. 过多的线程切换影响操作系统性能解决: 使用线程池, 处理不过来的放入队列, 再 ...

  6. Java NIO2 File API介绍

    Introduction to the Java NIO2 File API GitHub NIO2中的文件API是Java 7附带的Java平台的主要新功能之一,特别是新的文件系统API的一个子集以 ...

  7. 什么是NIO2

    NIO2I/O发展历史Java1.0-1.3在Java的早期版本中,没有完整的I/O支持,在开发过程中需要解决以下问题:1)没有数据缓冲区或者NIO的通道概念,需要编程人员处理底层细节.2)I/O是受 ...

  8. Java NIO2:缓冲区

    什么是缓冲区 一个缓冲区对象是固定数量的数据的容器,其作用是一个存储器,或者分段运输区,在这里数据可被存储并在之后用于检索.缓冲区像前篇文章讨论的那样被写满和释放,对于每个非布尔原始数据类型都有一个缓 ...

  9. java nio2

    Buffer的基本用法 使用Buffer读写数据一般遵循以下四个步骤: 写入数据到Buffer 调用flip()方法 从Buffer中读取数据 调用clear()方法或者compact()方法 当向b ...

随机推荐

  1. 准备CLR源码阅读环境

    微软发布了CLR 2.0的源码,这个源码是可以直接在freebsd和windows环境下编译及运行的,请在微软shared source cli(http://www.microsoft.com/en ...

  2. [Java入门笔记] 面向对象编程基础(二):方法详解

    什么是方法? 简介 在上一篇的blog中,我们知道了方法是类中的一个组成部分,是类或对象的行为特征的抽象. 无论是从语法和功能上来看,方法都有点类似与函数.但是,方法与传统的函数还是有着不同之处: 在 ...

  3. net与树莓派的情缘(一)

    想做个NAS  由于手中经济有限又不想花太多钱,所以决定买个树莓派自己搭建. 板子:树莓派2代 elem14的 内存卡:闪迪 class10 16g 网线一根,电源线一个 系统:树莓派官方 Raspb ...

  4. App.config应用的说明

    对访问数据库的链接字符串的封装(MS什么都在封装,弄的我们原来越方(弱)便(智)),好吧,你可以解释说可以方便的更改链接只更改配置,而不用动主程序------隔离(隔离--保护:搞过配电的应该不陌生吧 ...

  5. UI开发中的Unit test新工具:网页抓屏比较

    在UI开发中,判断是否正常往往需要看到UI长的模样,所以一般的Unit test无法胜任. 现在有一款通过抓屏而后相素比较的方法,或许对于UI自动化测试能够起到比较好的改进效果. 具体请参见:http ...

  6. [转]svn 命令大全

    博客地址:http://blog.csdn.net/gexiaobaohelloworld/article/details/7752862

  7. asp.net 的那点事(1、当用户在浏览器地址栏输入了网址后,发生了什么?)

    从今天开始我将抽出空闲时间复习asp.net相关知识.此篇博文只是为了记录学习当中的知识点和感觉到比较重要的知识点. 本人才疏学浅,如有遗漏或者错误希望广大博友尽情拍砖.我会在后续中进行更正. 这个问 ...

  8. BZOJ 1189: [HNOI2007]紧急疏散evacuate( BFS + 二分答案 + 匈牙利 )

    我们可以BFS出每个出口到每个人的最短距离, 然后二分答案, 假设当前答案为m, 把一个出口拆成m个表示m个时间, 点u到出口v的距离为d, 那么u->v的[d, m]所有点连边, 然后跑匈牙利 ...

  9. JS模式--通用对象池的实现

    var objectPoolFactory = function (createObjFn) { var objectPool = []; return { create: function () { ...

  10. java面向对象的思想(java三大特性)

    用通俗易懂的语言来理解java面向对象的思想 大家都知道,java是面向对象的编程,掌握面向对象的编程思想是掌握java编程语言的核心,但是很多人在面向对象方面都存在或多或少的误区,有的是刚学完C语言 ...