java核心技术-NIO

1、reactor(反应器)模式

  使用单线程模拟多线程,提高资源利用率和程序的效率,增加系统吞吐量。下面例子比较形象的说明了什么是反应器模式:

  一个老板经营一个饭店,

  传统模式 - 来一个客人安排一个服务员招呼,客人很满意;(相当于一个连接一个线程)

  后来客人越来越多,需要的服务员越来越多,资源条件不足以再请更多的服务员了,传统模式已经不能满足需求。老板之所以为老板自然有过人之处,老板发现,服务员在为客人服务时,当客人点菜的时候,服务员基本处于等待状态,(阻塞线程,不做事)。

  于是乎就让服务员在客人点菜的时候,去为其他客人服务,当客人菜点好后再招呼服务员即可。 --反应器(reactor)模式诞生了

  饭店的生意红红火火,几个服务员就足以支撑大量的客流量,老板用有限的资源赚了更多的money~~~~^_^

 通道:类似于流,但是可以异步读写数据(流只能同步读写),通道是双向的,(流是单向的),通道的数据总是要先读到一个buffer 或者 从一个buffer写入,即通道与buffer进行数据交互。

  

通道类型:  

FileChannel:从文件中读写数据。  
DatagramChannel:能通过UDP读写网络中的数据。  
SocketChannel:能通过TCP读写网络中的数据。  
ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。  
 - FileChannel比较特殊,它可以与通道进行数据交互, 不能切换到非阻塞模式,套接字通道可以切换到非阻塞模式;

缓冲区 - 本质上是一块可以存储数据的内存,被封装成了buffer对象而已!

  

缓冲区类型:

ByteBuffer  
MappedByteBuffer  
CharBuffer  
DoubleBuffer  
FloatBuffer  
IntBuffer  
LongBuffer  
ShortBuffer  
常用方法:

allocate() - 分配一块缓冲区  
put() - 向缓冲区写数据
get() - 向缓冲区读数据  
filp() - 将缓冲区从写模式切换到读模式  
clear() - 从读模式切换到写模式,不会清空数据,但后续写数据会覆盖原来的数据,即使有部分数据没有读,也会被遗忘;  
compact() - 从读数据切换到写模式,数据不会被清空,会将所有未读的数据copy到缓冲区头部,后续写数据不会覆盖,而是在这些数据之后写数据
mark() - 对position做出标记,配合reset使用
reset() - 将position置为标记值    
缓冲区的一些属性:

capacity - 缓冲区大小,无论是读模式还是写模式,此属性值不会变;
position - 写数据时,position表示当前写的位置,每写一个数据,会向下移动一个数据单元,初始为0;最大为capacity - 1,切换到读模式时,position会被置为0,表示当前读的位置

limit - 写模式下,limit 相当于capacity 表示最多可以写多少数据,切换到读模式时,limit 等于原先的position,表示最多可以读多少数据。

非直接缓冲区:通过allocate() 方法 分配缓冲区,将缓冲区建立在JVM内存中

直接缓冲区:通过allocateDirect() 方法直接缓冲区 将缓冲区建立在物理内存中

2.1 关于缓冲区各个属性的测试

    String str = "abcde";

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

    System.out.println("--------------allocate()----------------");
    System.out.println(buf.position());//0
    System.out.println(buf.limit());//1024
    System.out.println(buf.capacity());//1024

    //2. 利用put存入数据到缓冲区中去
    buf.put(str.getBytes());

    System.out.println("----------------put()-------------------");
    System.out.println(buf.position());//5
    System.out.println(buf.limit());//1024
    System.out.println(buf.capacity());//1024

    //3. 切换到读取模式
    buf.flip();

    System.out.println("----------------flip()------------------");
    System.out.println(buf.position());//0
    System.out.println(buf.limit());//5
    System.out.println(buf.capacity());//1024

    //4. 利用get() 读取缓冲区中的数据
    byte[] dst = new byte[buf.limit()];
    buf.get(dst);
    System.out.println(new String(dst,0,dst.length));

    System.out.println("----------------get()------------------");
    System.out.println(buf.position());//5
    System.out.println(buf.limit());//5
    System.out.println(buf.capacity());//1024

    //5.可重复读
    buf.rewind();

    System.out.println("----------------rewind()------------------");
    System.out.println(buf.position());//0
    System.out.println(buf.limit());//5
    System.out.println(buf.capacity());//1024

    //6.clear(): 清空缓冲区, 但是缓冲区的数据依然存在, 但是处于被遗忘的状态
    buf.clear();

    System.out.println("----------------clear()-------------------");
    System.out.println(buf.position());//0
    System.out.println(buf.limit());//1024
    System.out.println(buf.capacity());//1024

    byte[] newByte = new byte[buf.limit()];
    buf.get(newByte);
    System.out.println(new String(newByte,0,newByte.length));

2.2 关于通道的使用

1.利用通道进行 文件的复制 非直接缓冲区

    FileInputStream fis = null;
    FileOutputStream fos = null;
    FileChannel inChannel = null;
    FileChannel outChannel = null;
    try {
        fis = new FileInputStream("1.jpg");
        fos = new FileOutputStream("2.jpg");

        // ①获取通道
        inChannel = fis.getChannel();
        outChannel = fos.getChannel();

        // ②将通道中的数据存入缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        // 将通道中的数据存入缓冲区
        while (inChannel.read(byteBuffer) != -1) {
            byteBuffer.flip(); // 切换读取数据的模式
            outChannel.write(byteBuffer);
            byteBuffer.clear();
        }

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (inChannel != null) {
            try {
                inChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (outChannel != null) {
            try {
                outChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (fis != null) {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (fos != null) {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

2.通道之间的传输

CREATE_NEW:如果文件不存在就创建,存在就报错

CREATE:如果文件不存在就创建,存在创建(覆盖)

    FileChannel inChannel = null;
    FileChannel outChannel = null;
    try {
        inChannel = FileChannel.open(Paths.get("hello.txt"), StandardOpenOption.READ);
        outChannel = FileChannel.open(Paths.get("hello2.txt"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE_NEW);

        inChannel.transferTo(0, inChannel.size(), outChannel);
    } catch (Exception e) {
        e.printStackTrace();
    }  finally {

        if(inChannel != null){
            try {
                inChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if(outChannel != null){
            try {
                outChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
  1. 使用直接缓冲区完成内存文件的复制

    FileChannel inChannel = null;
    FileChannel outChannel = null;
    try {
        inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
        outChannel = FileChannel.open(Paths.get("x.jpg"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE_NEW);
    
        MappedByteBuffer inMappedBuffer = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
        MappedByteBuffer outMappedBuffer = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
    
        System.out.println(inMappedBuffer.limit());
        byte[] b = new byte[inMappedBuffer.limit()];;
        inMappedBuffer.get(b);
        outMappedBuffer.put(b);
    
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
    
        if(inChannel != null){
            try {
                inChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        if(outChannel != null){
            try {
                outChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    }

    2.3 重点 NIO-非阻塞IO

个人认为 NIO 最难的两点 一个是对于选择器和选择键的理解 其次是对于网络通信模型的理解

本章内容以防过长 只讲解 NIO 的使用方法 上述两点参看下回分解

阻塞IO示例:

//客户端
@Test
public void client() throws IOException{
    SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));

    FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);

    ByteBuffer buf = ByteBuffer.allocate(1024);

    while(inChannel.read(buf) != -1){
        buf.flip();
        sChannel.write(buf);
        buf.clear();
    }

    sChannel.shutdownOutput();

    //接收服务端的反馈
    int len = 0;
    while((len = sChannel.read(buf)) != -1){
        buf.flip();
        System.out.println(new String(buf.array(), 0, len));
        buf.clear();
    }

    inChannel.close();
    sChannel.close();
}

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

    FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);

    ssChannel.bind(new InetSocketAddress(9898));

    SocketChannel sChannel = ssChannel.accept();

    ByteBuffer buf = ByteBuffer.allocate(1024);

    while(sChannel.read(buf) != -1){
        buf.flip();
        outChannel.write(buf);
        buf.clear();
    }

    //发送反馈给客户端
    buf.put("服务端接收数据成功".getBytes());
    buf.flip();
    sChannel.write(buf);

    sChannel.close();
    outChannel.close();
    ssChannel.close();
}

非阻塞IO示例-TCP:

//客户端br/>@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();

    //5. 将通道注册到选择器上, 并且指定“监听接收事件”
    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. 取消选择键 SelectionKey
            it.remove();
        }
    }
}

非阻塞IO示例-UDP:

@Test
public void send() throws IOException{
    DatagramChannel dc = DatagramChannel.open();

    dc.configureBlocking(false);

    ByteBuffer buf = ByteBuffer.allocate(1024);

    Scanner scan = new Scanner(System.in);

    while(scan.hasNext()){
        String str = scan.next();
        buf.put((new Date().toString() + ":\n" + str).getBytes());
        buf.flip();
        dc.send(buf, new InetSocketAddress("127.0.0.1", 9898));
        buf.clear();
    }

    dc.close();
}

@Test
public void receive() throws IOException{
    DatagramChannel dc = DatagramChannel.open();

    dc.configureBlocking(false);

    dc.bind(new InetSocketAddress(9898));

    Selector selector = Selector.open();

    dc.register(selector, SelectionKey.OP_READ);

    while(selector.select() > 0){
        Iterator<SelectionKey> it = selector.selectedKeys().iterator();

        while(it.hasNext()){
            SelectionKey sk = it.next();

            if(sk.isReadable()){
                ByteBuffer buf = ByteBuffer.allocate(1024);

                dc.receive(buf);
                buf.flip();
                System.out.println(new String(buf.array(), 0, buf.limit()));
                buf.clear();
            }
        }

        it.rem    欢迎工作一到五年的Java工程师朋友们加入Java群: 891219277

群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

java核心技术-NIO

原文地址:http://blog.51cto.com/14084556/2324364

时间: 2024-11-06 11:49:20

java核心技术-NIO的相关文章

java核心技术卷一

java核心技术卷一 java基础类型 整型 数据类型 字节数 取值范围 int 4 +_2^4*8-1 short 2 +_2^2*8-1 long 8 +_2^8*8-1 byte 1 -128-127       浮点类型 数据类型 字节数 取值范围 小数位数 float 4 10^-38~10^38和-10^-38~-10^38 小数位数6-7 double 4 10^-308~10^308和-10^-308~-10^308 15位小数         boolean 类型和char 类

不惑JAVA之JAVA基础 - NIO (一)

JAVA中最可以大书特书的我觉得至少有两个:一个是NIO,另外一个就是JVM了.这也就是为什么一直我没有去写这两个知识点的原因,因为我一直找不出来一个可以在一篇博文中全部覆盖这个知识点的总结. 这两天翻了一下了JAVA中的圣经<think in java>和<Java核心技术>,虽然写的很好,但感觉写的也不是太符合我想一篇博文覆盖NIO知识点的要求.由于NIO本来就是技术难点,并且java对IO的设计和使用也较为复杂难懂.我也是能力有限如有说明不到位或错误的地方请大家指出. 本文参

java核心技术-(总结自杨晓峰-java核心技术36讲)

1. 谈谈你对java平台的理解 首先是java最显著的两个特性,一次写入处处运行:还有垃圾收集器gc,gc能够对java内存进行管理回收,程序员不需要关心内存的分配和回收问题 然后谈谈jre和jdk的区别,jre包含了jvm和java类库:jdk除了jvm和java类库,还包含了一些java工具集 常见的垃圾收集器有: Serial GC:串行收集,垃圾回收时会阻塞工作线程 Parallel GC:并行收集,多线程收集,停顿时间短,吞吐量高 CMS:使用标记清除算法,多线程进行垃圾收集 G1:

《Java核心技术 卷1 基础知识 原书第9版》pdf

下载地址:网盘下载 内容简介 编辑 CayS.Horstmann等编著,公飞编译的<Java核心技术>(CoreJava)自第1版出版以来,一直备受广大Java程序设计人员的青睐,畅销不衰,是Java经典书籍.第8版针对JavaSE6平台进行了全面更新,囊括了Java平台标准版(JavaSE/J2SE)的全部基础知识,提供了大量完整且具有实际意义的应用实例,详细介绍了Java语言基础知识.面向对象程序设计.接口与内部类.事件监听器模型.swing图形用户界面程序设计.打包应用程序.异常处理.登

java的nio之:java的nio系列教程之channel的概念

一:java的nio的channel Java NIO的通道类似流,但又有些不同: ==>既可以从通道中读取数据,又可以写数据到通道.但流的读写通常是单向的. ==>通道可以异步地读写. ==>通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入. 正如上面所说,从通道读取数据到缓冲区,从缓冲区写入数据到通道.如下图所示: 二:java的nio的channel的实现 这些是Java NIO中最重要的通道的实现: ==>FileChannel  : ==>

java核心技术学习笔记之一程序设计概述

Java 核心技术之一程序设计概述 一.   Java语言的特点 简单行 :取经于C++,排除了C++不常用的指针.结构等,增加垃圾回收. 面向对象:与C++不同是单继承,但是可以继承多接口.完全面向对象的语言: 网络技能:Socket,FTP,Http,URL编程简单: 健壮性:避免指针错误使用: 安全性:构建防病毒防篡改系统: 体系结构中立:字节码: 可移植性: 解释性:可以解释任何移植了的字节码: 高性能: 多线程 动态性: 二.   Internet 和 Java Applet 在网页中

Java核心技术-4-对象与类

4 对象与类 4.1 面向对象程序设计概述 1 类 封装(数据隐藏):将数据和行为组合在一个包中,并对对象使用者隐藏数据的实现方式. 对象中的数据成为实例域,操纵数据的过程称为方法. 2 对象 对象的三个主要特性:对象的行为behavior,对象的状态state,对象标识identity.3 识别类 4 类之间的关系 依赖uses-a,一个类的方法操纵另一个类的对象 聚合has-a,一个类的对象(其数据域)包含另一个类的对象 继承is-a,类A扩展类B. 4.2 使用预定义类 1 对象与对象变量

Java之NIO

想要学习Java的Socket通信,首先要学习Java的IO和NIO基础,这方面可以阅读<Java NIO 系列教程>. 下面展示自己代码熟悉Java的NIO编程的笔记. 1.缓冲区(Buffer) /* * 一.缓冲区(Buffer):在Java 中负责数据的存取.缓冲区就是数组.用于存储不同数据类型的数据 *        *       根据数据类型不同(boolean除外),提供了相应类型的缓冲区 *         ByteBuffer 那么实际上最常用的,因为网络传输的字节就是By

java的nio之:java的nio系列教程之概述

一:java的nio的核心组件?Java NIO 由以下几个核心部分组成: ==>Channels ==>Buffers ==>Selectors 虽然Java NIO 中除此之外还有很多类和组件,但在我看来,Channel,Buffer 和 Selector 构成了核心的API.其它组件,如Pipe和FileLock,只不过是与三个核心组件共同使用的工具类.因此,在概述中我将集中在这三个组件上.其它组 件会在单独的章节中讲到. Channel 和 Buffer 基本上,所有的 IO 在