信息与通信工程学院1非阻塞通信的用法信息与通信工程学院胡铮huzheng@bupt.edu.cn2主要内容非阻塞的概念Java的非阻塞模式的支持类编程范例服务器客户端3线程阻塞的概念(1)线程在运行中会因为某些原因而阻塞,共同特征:放弃CPU,暂停运行只有等到导致阻塞的原因消除,才能恢复运行。或者被其他线程中断,退出阻塞状态,抛出InterruptedException4线程阻塞的概念(2)线程阻塞的原因:执行了Thread.sleep(intn)方法要执行一段同步代码,无法获得相关的同步锁执行了一个对象的wait()方法,等待notify()或notifyAll()方法,才可能将其唤醒执行I/O操作或进行远程通信时,会因为等待相关的资源而阻塞。如:System.in.read()5线程阻塞的概念(3)客户通信程序调用构造函数、或者connect()方法,直到连接成功从Socket的输入流读入数据向Socket的输出流写数据setSoLinger方法设置了关闭Socket的延迟时间后,close()方法会进入阻塞服务器程序ServerSocket的accept()方法从Socket输入流输入,以及从输出流泻数据6多线程处理阻塞通信创建ServerSocket接收连接:accept()把任务委派给工作线程等待任务通信是否结束接收请求数据发送响应数据关闭连接是否主线程工作线程7这些网络服务的底层都离不开对socket的操作。他们都有一个共同的结构:1.Readrequest2.Decoderequest3.Processservice4.Encodereply5.Sendreply8局限分析工作线程并不是越多越好Java虚拟机会为每个线程分配独立的堆栈空间,工作线程数目越多,开销越大,增加JVM调度线程的负担,增加线程同步的复杂性,提高了线程死锁的可能性工作线程的许多时间都浪费在阻塞I/O操作上,Java虚拟机需要频繁地转让CPU的使用权,使进入阻塞状态的线程放弃CPU,再把CPU分配给处于可运行状态的线程9非阻塞通信的基本思想举例烧开水煮粥锅里放水,打开煤气炉等待水烧开//阻塞关闭煤气炉,把开水灌到壶里锅里放水和米,打开煤气炉等待粥烧开//阻塞调小煤气炉火力等待粥烧熟//阻塞关闭煤气炉10锅里放水,打开煤气炉//开始烧开水锅里放水和米,打开煤气炉//开始烧粥while(一直等待,直到有水烧开、粥烧开或粥烧熟事件发生){//阻塞if(水烧开){关闭煤气炉,把开水灌到壶里;}if(粥烧开){调小煤气炉火力;}if(粥烧熟){关闭煤气炉;}if(水烧开且粥已经烧熟){退出循环;}}11非阻塞服务器模式只需要一个线程就能同时负责接收客户的连接、接收各个客户发送的数据,以及向各个客户发送响应数据while(一直等待,直到有接收连接就绪事件、读就绪事件或写就绪事件发生)//阻塞{if(有客户连接)接受客户的连接;//非阻塞if(某个socket的输入流有可读数据)从输入流中读数据;//非阻塞if(某个Socket的输出流可以写数据)向输出流写数据;//非阻塞}12设计背后的基石反应器模式,REACTORpattern,类似于Observer模式用于事件多路分离和分派的体系结构模式。反应器模式的核心功能事件多路分用将事件分派到各自相应的事件处理程序Reactor模式类似于AWT中的Event处理事件触发机制是最好的解决办法,当有事件发生时,会触动handler,然后开始数据的处理。13题外话设计模式偏向于架构的编程模式,使用什么样的结构和用法来完成功能了解模式的用意、结构,以及这一模式适合于什么样的情况等推荐书籍Java与模式——阎宏14Java非阻塞通信支持java.nio包提供了支持非阻塞通信的类ServerSocketChannel:替代ServerSocket,支持阻塞通信和非阻塞通信;SocketChannel:替代Socket,支持阻塞通信和非阻塞通信Selector:为ServerSocketChannel监控接收连接就绪事件,为SocketChannel监控连接就绪、读就绪和写就绪事件SelectionKey:代表ServerSocketChannel及SocketChannel向Selector注册事件的句柄。当一个SelectionKey对象位于Selector对象的selected-keys集合中时,就表示与这个SelectionKey对象相关的事件发生了。15SelectableChannelServerSocketChannel与SocketChannel都是SelectableChannel的子类SelectableChannel类及其子类都能委托Selector来监控它们可能发生的一些事情,这个过程称为注册事件过程。SelectableChannelInterfaceByteChannelInterfaceChannelSocketChannelServerSocketChannel16内存Channel(通道)用来连接缓冲区与数据源/汇支持非阻塞读写通道数据源缓冲区通道数据汇17NIO的非阻塞I/O机制是围绕选择器和通道构建的。Channel类表示服务器和客户机之间的一种通信机制。Selector类是Channel的事件多路分离器(EventDemultiplexer),将传入客户机请求多路分用并将它们分派到各自的请求处理程序。NIO是一个基于事件的IO架构,最基本的思想就是:有事件我通知你,你再去做你的事情.而且NIO的主线程只有一个,不像传统的模型,需要多个线程以应对客户端请求,也减轻了JVM的工作量。当Channel注册至Selector以后,经典的调用方法如下:while(somecondition){intn=selector.select(TIMEOUT);//取得时间通知if(n==0)continue;//如果大于零,即有事件发生for(Iteratoriter=selector.selectedKeys().iterator();iter.hasNext();){//依次取得事件,并判断各事件类型,根据key中的表示事件,来做相应的处理if(key.isAcceptable())doAcceptable(key);if(key.isConnectable())doConnectable(key);if(key.isValid()&&key.isReadable())doReadable(key);if(key.isValid()&&key.isWritable())doWritable(key);iter.remove();}}异步socket的核心,即异步socket不过是将多个socket的调度(或者还有他们的线程调度)全部交给操作系统自己去完成。Selector,不过是将这些调度收集、分发而已。18SelectableChannel支持阻塞I/O和非阻塞I/O的通道方法设置为阻塞/非阻塞:configureBlocking(booleanblock)注册事件register(Selectorsel,intops)register(Selectorsel,intops,Objectattachment)返回一个SelectionKey对象,用来跟踪被注册的事件;attachment用于为SelectionKey关联一个附件,从中可以得到包含处理与这个事件有关的信息19MyHandlerhandler=newMyHandler();SelectionKeykey=socketChannel.register(selector,SelectionKey.OP_READ|SelectionKey.OP_WRITE,handler);等价于SelectionKeykey=socketChannel.register(selector,SelectionKey.OP_READ|SelectionKey.OP_WRITE);MyHandlerhandler=newMyHandler();key.attatch(handler);20ServerSocketChannel除了继承的,主要是SocketChannelaccept();阻塞或非阻塞,返回的SocketChannel对象是处于阻塞模式的ServerSocketChannelopen():静态方法创建对象ServerScoketsocket():返回关联的ServerSocket对象21SocketChannel静态方法SocketChannelopen()//返回阻塞模式的SocketChannelSocketsocket()//返回关联的Socket对象booleanconnect()booleanfinishConnect()intread(ByteBufferdst)intwrite(ByteBuffersrc)22Selector类all-keys集合子集:selected-keys集合,cancelled-keys集合主要参见api参考intselect()intselect(longtimeout)Selectorwakeup()voidclose()23SelctionKey类在对象有效期间,会一直监控与SelectionKey对象相关的事件失效:程序调用SelectionKey的cancel()方法;关闭与SelectionKey关联的Channel;与Selectionkey关联的Selector被关闭事件ServerSocketChannel的事件:SelectionKey.OP_ACCEPT:接收连接就绪:常量16SocketChannel的事件:SelectionKey.OP_CONNECT:连接就绪事件:常量8SelectionKey.OP_READ:读就绪事件:常量1SelectionKey.OP_WRITE:写就绪事件:常量424SelectionKey、Selector与SelectableChannel之间的关联关系SelectableChannelSelectionKeySelectorSocketChannelServerSocketChannel10...n125还需关注的(1)——Buffer缓冲区Buffer的使用两个方面提高IO操作的效率(减少实际的物理读写次数;减少动态分配和回收内存区域的次数)Java.nio包公开buffer类的APIbuffer的几个属性(都是非负值)容量(capacity):表示该缓冲区可以保存多少数据极限(limit):表示缓冲区目前的使用终点,使缓冲区便于重用位置(position):表示缓冲区中下一个读写单元的位置容量=极限=位置=0改变3个属性的方法clear():把极限设为容量,把位置设为0;flip():把极限设为位置,再把位置设为0;rewind():不改变极限,把位置设为0;remaining():返回缓冲区的剩余容量,取值等于limit-positioncompact():删除0到位置的内容,然后把当前位置到极限limit的内容复制到0到limit-positon的区域内。当前位置position和极限limit的取值也作相应的变化26ByteBuffer实例的方法ByteBuffer方法准备Buffer以实现结果值PositionLimitByteBufferclear()将数据read()/put()进缓冲区0capacityByteBufferflip()从缓冲区write()/get()0positionByteBufferrewind()从缓冲区rewrite()/get()0unchanged27Buffer类的层次结构28需要关注的(2)字符编码Charsetjava.nio.Charset解码:字符编码字符串Charbufferdecode(ByteBufferbb)编码:字符串字符编码Byte