实时搜索: nio为什么不阻塞

nio为什么不阻塞

423条评论 3838人喜欢 3795次阅读 577人点赞
现在的通讯基本上都是用NIO的非堵塞模式来与服务器通讯发送消息包,socketchannel发送数据包时,并没有等待服务器的恢复就会返回,无法判断该条消息是否发送成功,服务器是否收到?作为实时通讯的工具,这样很不好。对NIO socketchannel比较熟悉的高手,有没有好的解决方案,希望指点下! ...

4.socket框架netty的使用,以及nio的实现原理,为什么是异步非阻塞: socket框架netty的使用,以及nio的实现原理,为什么是异步非阻塞
按Alt+F可进入File菜单, 如图:
File菜单的子菜单共有9项,分别叙述如下:
1. Load:装入一个文件, 可用类似DOS的通配符(如*.C)来进行列表选择。也可装入其它扩展名的文件, 只要给出文件名(或只给路径)即可。该项的热键为F3, 即只要按F3即可进入该项, 而不需要先进入File菜单再选此项。
2. Pick:将最近装入编辑窗口的8个文件列成一个表让用户选择, 选择后将该程序装入编辑区, 并将光标置在上次修改过的地方。其热健为Alt-F3。
3. New:新建文件, 缺省文件名为NONAME.C, 存盘时可改名。
4. Save:将编辑区中的文件存盘, 若文件名是NONAME.C时, 将询问是否更改文件名, 其热键为F2。
5. Write to:可由用户给出文件名将编辑区中的文件存盘, 若该文件已存在, 则询问要不要 覆盖。
6. Directory:显示目录及目录中的文件, 并可由用户选择。
7. Change dir:显示当前默认目录, 用户可以改变默认目录。
8. Os shell:暂时退出Turbo C 2.0到DOS提示符下, 此时可以运行DOS 命令, 若想回到 Turbo C 2.0中, 只要在DOS状态下键入EXIT即可。
9. Quit:退出Turbo C 2.0, 返回到DOS操作系统中, 其热键为Alt+X。

NIO Socketchannel 如何判断发包是否成功?(非堵塞模式): 这个没用过,如果你用TCP的话,会有状态的

ftpclient 用的是nio还是io: nio是new io的简称,从jdk1.4就被引入了。现在的jdk已经到了1.6了,可以说不是什么新东西了。但其中的一些思想值得我来研究。这两天,我研究了下其中的套接字部分,有一些心得,在此分享。
首先先分析下:为什么要nio套接字?
nio的主要作用就是用来解决速度差异的。举个例子:计算机处理的速度,和用户按键盘的速度。这两者的速度相差悬殊。如果按照经典的方法:一个用户设定一个线程,专门等待用户的输入,无形中就造成了严重的资源浪费:每一个线程都需要珍贵的cpu时间片,由于速度差异造成了在这个交互线程中的cpu都用来等待。
nio套接字是怎么做到的?
其实,其中的思想很简单:轮询。一个线程轮询多个input;传统的方式是:有n个客户端就要有n个服务线程+一个监听线程,现在采取这种凡是,可以仅仅使用1个线程来代替n个服务线程以此来解决。
具体应用例子:
在ftp的控制连接中,因为只有少量的字符命令进行传输,所以可以考虑利用这种轮询的方式实现,以节省资源。

-----------------------------------------------------
Java中的阻塞和非阻塞IO包各自的优劣思考
NIO 设计背后的基石:反应器模式,用于事件多路分离和分派的体系结构模式。

反应器(Reactor):用于事件多路分离和分派的体系结构模式

通常的,对一个文件描述符指定的文件或设备, 有两种工作方式: 阻塞 与非阻塞 。所谓阻塞方式的意思是指, 当试图对该文件描述符进行读写时, 如果当时没有东西可读,或者暂时不可写, 程序就进入等待 状态, 直到有东西可读或者可写为止。而对于非阻塞状态, 如果没有东西可读, 或者不可写, 读写函数马上返回, 而不会等待 。

一种常用做法是:每建立一个Socket连接时,同时创建一个新线程对该Socket进行单独通信(采用阻塞的方式通信)。这种方式具有很高的响应速度,并且控制起来也很简单,在连接数较少的时候非常有效,但是如果对每一个连接都产生一个线程的无疑是对系统资源的一种浪费,如果连接数较多将会出现资源不足的情况。

另一种较高效的做法是:服务器端保存一个Socket连接列表,然后对这个列表进行轮询,如果发现某个Socket端口上有数据可读时(读就绪),则调用该socket连接的相应读操作;如果发现某个 Socket端口上有数据可写时(写就绪),则调用该socket连接的相应写操作;如果某个端口的Socket连接已经中断,则调用相应的析构方法关闭该端口。这样能充分利用服务器资源,效率得到了很大提高。

传统的阻塞式IO,每个连接必须要开一个线程来处理,并且没处理完线程不能退出。

非阻塞式IO,由于基于反应器模式,用于事件多路分离和分派的体系结构模式,所以可以利用线程池来处理。事件来了就处理,处理完了就把线程归还。而传统阻塞方式不能使用线程池来处理,假设当前有10000个连接,非阻塞方式可能用1000个线程的线程池就搞定了,而传统阻塞方式就需要开10000个来处理。如果连接数较多将会出现资源不足的情况。非阻塞的核心优势就在这里。

为什么会这样,下面就对他们做进一步细致具体的分析:

首先,我们来分析传统阻塞式IO的瓶颈在哪里。在连接数不多的情况下,传统IO编写容易方便使用。但是随着连接数的增多,问题传统IO就不行了。因为前面说过,传统IO处理每个连接都要消耗 一个线程,而程序的效率当线程数不多时是随着线程数的增加而增加,但是到一定的数量之后,是随着线程数的增加而减少。这里我们得出结论,传统阻塞式IO的瓶颈在于不能处理过多的连接。

然后,非阻塞式IO的出现的目的就是为了解决这个瓶颈。而非阻塞式IO是怎么实现的呢?非阻塞IO处理连接的线程数和连接数没有联系,也就是说处理10000个连接非阻塞IO不需要10000个线程,你可以用1000个也可以用2000个线程来处理。因为非阻塞IO处理连接是异步的。当某个连接发送请求到服务器,服务器把这个连接请求当作一个请求"事件",并把这个"事件"分配给相应的函数处理。我们可以把这个处理函数放到线程中去执行,执行完就把线程归还。这样一个线程就可以异步的处理多个事件。而阻塞式IO的线程的大部分时间都浪费在等待请求上了。

Java NIO与IO的区别和比较: 1、面向流与面向缓冲
Java IO和NIO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
2、阻塞与非阻塞IO
Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
3、选择器(Selectors)
Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

java中IO和NIO的区别和适用场景:

java.NIO包里包括三个基本的组件

l buffer:因为NIO是基于缓冲的,所以buffer是最底层的必要类,这也是IO和NIO的根本不同,虽然stream等有buffer开头的扩展类,但只是流的包装类,还是从流读到缓冲区,而NIO却是直接读到buffer中进行操作。

因为读取的都是字节,所以在操作文字时,要用charset类进行编解码操作。

l channel:类似于IO的stream,但是不同的是除了FileChannel,其他的channel都能以非阻塞状态运行。FileChannel执行的是文件的操作,可以直接DMA操作内存而不依赖于CPU。其他比如socketchannel就可以在数据准备好时才进行调用。

l selector:用于分发请求到不同的channel,这样才能确保channel不处于阻塞状态就可以收发消息。


面向流与面向缓冲

Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java
IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

补充一点:NIO的buffer可以使用直接内存缓冲区,该缓冲区不在JVM中,性能会比JVM的缓冲区略好,不过会增加相应的垃圾回收的负担,因为JVM缓冲区的性能已经足够好,所以除非在对缓冲有特别要求的地方使用直接缓冲区,尽量使用JVM缓冲。

阻塞与非阻塞

Java IO是阻塞式的操作,当一个inputstream或outputstream在进行read()或write()操作时,是一直处于等待状态的,直到有数据读/写入后才进行处理.而NIO是非阻塞式的,当进行读写操作时,只会返回当前已经准备好的数据,没有就返回空,这样当前线程就可以处理其他的事情,提高了资源的使用率.

与传统IO的优势

在老的IO包中,serverSocket和socket都是阻塞式的,因此一旦有大规模的并发行为,而每一个访问都会开启一个新线程。这时会有大规模的线程上下文切换操作(因为都在等待,所以资源全都被已有的线程吃掉了),这时无论是等待的线程还是正在处理的线程,响应率都会下降,并且会影响新的线程。

 

而NIO包中的serverSocket和socket就不是这样,只要注册到一个selector中,当有数据放入通道的时候,selector就会得知哪些channel就绪,这时就可以做响应的处理,这样服务端只有一个线程就可以处理大部分情况(当然有些持续性操作,比如上传下载一个大文件,用NIO的方式不会比IO好)。

通过两个图的比较,可以看出IO是直连的,每个请求都给一条线程来处理,但是NIO却是基于反应堆(selector)来处理,直到读写的数据准备好后,才会通知相应的线程来进行处理。一言以蔽之:“selector不会让channel白占资源,没事的时候给我去睡觉。”

PS:NIO基于字节进行传输,在IO时要注意decode/encode。


更具体的信息请参阅:http://blog.csdn.net/zhansong_1987/article/details/45873861

Java 读写文件有非阻塞的 API 吗:

NIO

软件包 java.nio 的描述

定义作为数据容器的缓冲区,并提供其他 NIO 包的概述。

NIO API 的集中抽象为:

缓冲区,它们是数据容器;

字符集 及其相关解码器 和编码器,
它们在字节和 Unicode 字符之间进行转换;

各种类型的通道,它们表示到能够执行 IO
操作的
实体的连接;以及选择器 和选择键,它们与
可选择信道 一起定义了多路的、无阻塞的
I/O 设施。

java.nio 包定义了缓冲区类,这些类用于所有 NIO API。java.nio.charset 包中定义了字符集 API,java.nio.channels 包中定义了信道和选择器 API。每个子包都具有自己的服务提供者接口 (SPI) 子包,SPI 子包的内容可用于扩展平台的默认实现或构造替代实现。

缓冲区

描述

Buffer    位置,界限和容量;
清除,反转,重绕和标记/重置    

ByteBuffer    Get/put,压缩,查看;分配,包装    

MappedByteBuffer      映射到文件的字节缓冲区    

CharBuffer    Get/put,压缩;分配,包装    

DoubleBuffer        ' '    

FloatBuffer        ' '    

IntBuffer        ' '    

LongBuffer        ' '    

ShortBuffer        ' '    

ByteOrder    字节顺序的类型安全的枚举    

缓冲区 是一个固定数据量的指定基本类型的数据容器。除内容之外,缓冲区还具有位置 和界限,其中位置是要读写的下一个元素的索引,界限是第一个应该读写的元素的索引。基本 Buffer 类定义了这些属性以及清除、反转 和重绕 方法,用以标记 当前位置,以及将当前位置重置 为前一个标记处。

每个非布尔基本类型都有一个缓冲区类。每个类定义了一系列用于将数据移出或移入缓冲区的 get 和 put 方法,用于压缩、复制 和切片 缓冲区的方法,以及用于分配 新缓冲区和将现有数组包装 到缓冲区中的静态方法。

因为字节缓冲区可以用作 I/O 操作的源缓冲区和目标缓冲区,所以可以对它们加以区分。它们还支持其他缓冲区类所没有的几个特性:

可以将字节缓冲区分配为一个直接 缓冲区,在这种情况下,Java
虚拟机将最大限度地直接在缓冲区上执行本机 I/O 操作。

可以通过 mapping 将文件区域直接包装到内存中来创建字节缓冲区,在这种情况下,可以使用 MappedByteBuffer 类中定义的几个其他文件相关的操作。

字节缓冲区提供了对其内容的访问(其内容作为任何非布尔基本类型的异类或同类二进制数据序列),访问要么是以 big-endian 字节顺序进行,要么是以
little-endian 字节顺序进行。

除非另有说明,否则向此包的任何类或接口中的构造方法或方法传递 null 变量,都会抛出 NullPointerException。  

java nio中selector.select()是唯一的阻塞方法么?:

NIO是非阻塞IO,传统的BIO是每次request服务器都会分配一个线程,所以,当某个线程发生IO阻塞的时候,该线程就无法充分利用。而NIO则是服务器会不断的轮询每一个client发送的request,如果有N个可读状态的request,OK,那服务器就会分配N个线程去读,所谓非阻塞就是这个意思。

如果理解NIO的意思,楼主的问题自然而然就解答了。

selector是怎样检查有没有就绪事件的?答:每个request会封装一个channel,将所有的channel注册在一个Selector上,然后selector开始不断的轮询每个request的可读状态,如果可读,则直接返回可读状态的channel。

隔段时间检查一下吗?答:是不断轮询可读状态的,if(n==0)continue;如果可读状态的个数n为0,则继续轮询,这句话就是这个意思。

java nio 非阻塞读写具体应该怎么操作,能否给个例子程序: package com.java.xiong.Net17;
 
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
 
public class NServer {
 
    // 用于检测所有的Channel状态的selector
    private Selector selector = null;
    static final int PORT = 30000;
    // 定义实现编码、解码的字符串集对象
    private Charset charse = Charset.forName("GBK");
 
    public void init() throws IOException {
        selector = Selector.open();
        // 通过open方法来打开一个未绑定的ServerSocketChannel是咧
        ServerSocketChannel server = ServerSocketChannel.open();
        InetSocketAddress isa = new InetSocketAddress("127.0.0.1", PORT);
        // 将该ServerSocketChannel绑定到指定的IP地址
        server.bind(isa);
        // 设置serverSocket已非阻塞方式工作
        server.configureBlocking(false);
        // 将server注册到指定的selector对象
        server.register(selector, SelectionKey.OP_ACCEPT);
        while (selector.select() > 0) {
            // 一次处理selector上的每个选择的SelectionKey
            for (SelectionKey sk : selector.selectedKeys()) {
                // 从selector上已选择的Kye集中删除正在处理的SelectionKey
                selector.selectedKeys().remove(sk);
                // 如果sk对应的Channel包含客户端的连接请求
                if (sk.isAcceptable()) {
                    // 调用accept方法接收连接,产生服务器段的SocketChennal
                    SocketChannel sc = server.accept();
                    // 设置采用非阻塞模式
                    sc.configureBlocking(false);
                    // 将该SocketChannel注册到selector
                    sc.register(selector, SelectionKey.OP_READ);
                }
                // 如果sk对应的Channel有数据需要读取
                if (sk.isReadable()) {
                    // 获取该SelectionKey对银行的Channel,该Channel中有刻度的数据
                    SocketChannel sc = (SocketChannel) sk.channel();
                    // 定义备注执行读取数据源的ByteBuffer
                    ByteBuffer buff = ByteBuffer.allocate(1024);
                    String content = "";
                    // 开始读取数据
                    try {
                        while (sc.read(buff) > 0) {
                            buff.flip();
                            content += charse.decode(buff);
                        }
                        System.out.println("读取的数据:" + content);
                        // 将sk对应的Channel设置成准备下一次读取
                        sk.interestOps(SelectionKey.OP_READ);
                    }
                    // 如果捕获到该sk对银行的Channel出现了异常,表明
                    // Channel对应的Client出现了问题,所以从Selector中取消
                    catch (IOException io) {
                        // 从Selector中删除指定的SelectionKey
                        sk.cancel();
                        if (sk.channel() != null) {
                            sk.channel().close();
                        }
                    }
                    // 如果content的长度大于0,则连天信息不为空
                    if (content.length() > 0) {
                        // 遍历selector里注册的所有SelectionKey
                        for (SelectionKey key : selector.keys()) {
                            // 获取该key对应的Channel
                            Channel targerChannel = key.channel();
                            // 如果该Channel是SocketChannel对象
                            if (targerChannel instanceof SocketChannel) {
                                // 将读取到的内容写入该Channel中
                                SocketChannel dest = (SocketChannel) targerChannel;
                                dest.write(charse.encode(content));
                            }
                        }
                    }
                }
            }
        }
 
    }
     
    public static void main(String [] args) throws IOException{
        new NServer().init();
    }
 
}

  • psn有几个服

    今天北京国安在工人体育场的足球晚上几点钟开始: 今晚19:30分,北京国安对水源三星。 ...

    797条评论 1518人喜欢 5831次阅读 224人点赞
  • 15款福克斯怎样

    深圳信托公司有哪些: 看门狗财富为您解答。深圳有十家信托公司,以下不分排名:1.华润深国投信托有限公司2.中国对外经济贸易信托有限公司3.中国民生信托有限公司4.中铁信托有限责任公司5.华澳国际信托有限公司6.华融国际信托有限责任公司7....

    322条评论 6137人喜欢 6035次阅读 774人点赞
  • iphone 哪里剪卡

    2014国家乡镇助理医师资格分数什么时候查: 2014年乡镇助理医师考试的分数线分别为:临床类别145分,中医类别102分。2015年一月,2014国家执业医师资格考试分数线公布:临床执业医师:363临床执业助理医师:176口腔执业医师:359口腔执业助理医师:...

    556条评论 6343人喜欢 4169次阅读 692人点赞
  • 2016退休金涨多少

    电脑上的软件,,有没有办法在云端让它一直运行,,在自己电脑上关机了就不能运行了,,就是想让它在网上: 网站空间可以运行软件,不过有些网站对具体的程序软件有一些限制,阿里云服务器ecs就是这类产品,功能相当于一台简易网络电脑,有些功能有一定局限,就一直运行着。 ...

    905条评论 5506人喜欢 4998次阅读 642人点赞
  • 11.17是什么星座

    E8400配内存和主板问题。: 没有任何道理,那些说双通道提升带宽,总线=内存频率的简直就是放PLZ记住一个公式总线=内存频率*2=CPU外频*4,此时整个系统可以达到最佳化,没有一点瓶颈也没有浪费至于内存=总线,这是N年前的说法,早就淘汰了也就是...

    464条评论 3020人喜欢 2962次阅读 882人点赞