Nio使用

Java NIO带来了什么?

  传统Java IO在我前一篇博文细说Java IO相关已经介绍过了,它是阻塞的,低效的。那么Java NIO和传统Java IO有什么不同?带来了什么?

(1)面向块的I/O

  传统JavaIO是面向流的I/O。流I/O一次处理一个字节。NIO则是面向块的I/O,每次操作都是以数据块为单位。它们的差距就好象两个人吃饭,一个人一粒一粒的吃,另一个人狼吞虎咽,快慢显而易见。

  NIO中引入了缓冲区(Buffer)的概念,缓冲区作为传输数据的基本单位块,所有对数据的操作都是基于将数据移进/移出缓冲区而来;读数据的时候从缓冲区中取,写的时候将数据填入缓冲区。尽管传统JavaIO中也有相应的缓冲区过滤器流(BufferedInputStream等),但是移进/移出的操作是由程序员来包装的,它本质是对数据结构化和积累达到处理时的方便,并不是一种提高I/O效率的措施。NIO的缓冲区则不然,对缓冲区的移进/移出操作是由底层操作系统来实现的。

  通常一次缓冲区操作是这样的:某个进程需要进行I/O操作,它执行了一次读(read)或者写(write)的系统调用,向底层操作系统发出了请求,操作系统会按要求把数据缓冲区填满或者排干。说起来简单,其实很复杂。但至少我们知道了这事是由操作系统干的,比我们代码级的实现要高效的多。

  除了效率上的差别外,缓冲区在数据分析和处理上也带来的很大的便利和灵活性。

 (2)非阻塞的I/O + 就绪性选择

  传统JavaIO是基于阻塞I/O模型的:当发起一个I/O请求时,如果数据没有准备好(read时无可读数据,write时数据不可写入),那么线程便会阻塞,直到数据准备好,导致线程大部分的时间都在阻塞。

  而非阻塞I/O则允许线程在有数据的时候处理数据,没有数据的时候干点别的,提高了资源利用率。

  就绪性选择通常是建立在非阻塞的基础上,并且更进一步,它把检查哪些I/O请求的数据准备好这个任务交给了底层操作系统,操作系统会去查看并返回结果集合,这样我们只需要关心那些准备好进行操作的IO通道。关于就绪性选择的过程会在后面详述。

  NIO提供的Socket可以用非阻塞的方式工作,并且支持就绪性选择,减少了资源消耗和CPU在线程间的切换,在管理线程效率上比传统Socket高。

(3)文件锁定和内存映射文件等操作系统特性

  NIO同时带来了很多当今操作系统大都支持的特性。

  文件锁定是多个进程协同工作的情况下,要协调进程间对共享数据的访问必不可少的工具。

  内存映射利用虚拟内存技术提供对文件的高速缓存,使读取磁盘文件就像从内存中读取一样高效,但是却不会有内存泄漏的危险,因为在内存中不会存在文件的完整拷贝。

  此外还有一些其他的特性,后面再详述。

 

为什么要使用NIO?

  显然,使用或者不使用NIO的理由不会是因为技术崇拜,因为这个东西才出来,看起来很酷我就去使用它。好吧,我承认我是有一点这样的原因。

  对于文件I/O, 在我看来使用IO和NIO是区别不大的,Java1.4开始原始IO也根据NIO重新实现过了,提供了对于NIO特性的支持。即使是流,也会比以前更加高效。企业级应用软件中涉及I/O的部分多半是读写文件的功能性需求,很少有在并发上的要求,那么JavaIO包已经很胜任了。

  对于网络I/O,传统的阻塞式I/O,一个线程对应一个连接,采用线程池的模式在大部分场景下简单高效。当连接数茫茫多时,并且数据的移动非常频繁,NIO无疑是更好的选择。

  NIO标榜的是高速、可伸缩的I/O,因为它更亲近操作系统。当需求很平凡,没有太高的效率要求的时候,你看不出它的好,反而觉得NIO代码实现复杂,不易理解。选择与否全看使用的场景,这点就看使用者的权衡了。


拷贝文件的例子:

代码演示:

public class FileReadByNio
{
    public static void readFile(String filePath,String destPath)
    {
        FileInputStream in = null;
        FileOutputStream out = null;
        try
        {
            // 获取源文件和目标文件的输入输出流  
            in = new FileInputStream(filePath);
            out = new FileOutputStream(destPath);
            // 获取输入输出通道
            FileChannel fcIn = in.getChannel();
            FileChannel fcOut = out.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            while (true)
            {
                // clear方法重设缓冲区,使它可以接受读入的数据
                buffer.clear();
                // 从输入通道中将数据读到缓冲区
                int r = fcIn.read(buffer);
                if (r == -1)
                {
                    break;
                }
                // flip方法让缓冲区可以将新读入的数据写入另一个通道  
                buffer.flip();
                fcOut.write(buffer);
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            try
            {
                if (in != null)
                {
                    in.close();
                }
                if (out != null)
                {
                    out.close();
                }
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args)
    {
        String filePath = "E:/test/student7.txt";
        String destPath = "E:/test/copystudent7.txt";
        readFile(filePath,destPath);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值