JAVA NET PROGRAM LEARN NOTE

非阻塞通信

线程阻塞的概念

线程阻塞的原因

导致线程阻塞的原因主要有以下几个方面

线程执行了Thread.sleep(int n)方法,线程放弃CPU,睡眠n毫秒,然后恢复运行。

线程要执行一段同步代码,由于无法获得相关的同步锁,只好进入阻塞状态,等到获得了同步锁,才能恢复运行。

线程执行了一个对象的wait()方法,进入阻塞状态,只有等到其他线程执行了该对象的notify()或notifyAll()方法,才可能将其唤醒。

线程执行I/O操作或进行远程通信时,会因为等待相关的资源而进入阻塞状态。

进行远程通信时,在客户程序中,线程在以下情况可能进入阻塞状态。

请求与服务器建立连接时,即当线程执行Socket的带参数的构造方法,或执行Socket的connect()方法时,会进入阻塞状态,直到连接成功,此线程才从Socket的构造方法或connect()方法返回。

线程从Socket的输入流读入数据时,如果没有足够的数据,就会进入阻塞状态,直到读入了足够的数据,或者到达输入流的末尾,或者出现了异常,才从输入流的read()方法返回或异常中断。

线程向Socket的输出流写一批数据时,可能会进入阻塞状态,等到输入出了所有的数据,或者出现异常,才从输出流的write()方法返回或异常中断。

调用Socket的setSoLinger()方法设置了关闭Socket的延迟时间,那么当线程执行Socket的close()方法时,会进入阻塞状态,直到底层Socket发送完所有剩余数据,或者超过了setSoLinger()方法设置的延时时间,才从close()方法返回。

在服务器程序中,线程在以下情况下可能会进入阻塞状态

线程执行ServerSocket的accept()方法,等待客户的连接,直到接受到了客户连接,才从accept()方法返回。

线程从Socket的输入流读入数据时,如果输入流没有足够的数据,就会进入阻塞状态。

线程向socket的输出流写一批数据时,可能会进入阻塞状态,等到输出了所有的数据,或者出现异常,才从输出流的write()方法返回或异常中断。


java.nio包提供了支持非阻塞通信的类

ServerSocketChannel:ServerSocket的替代类,支持阻塞通信与非阻塞通信。

SocketChannel:Socket的替代类,支持阻塞通信与非阻塞通信。

Selector:为ServerSocketChannel监控接收链接就绪事件,为SocketChannel监控链接就绪、读就绪和写就绪事件。

SelectionKey:代表ServerSocketChannel及SocketChannel向Selector注册事件的句柄。当一个SelectionKey对象位于Selector对象的selected-keys集合中时,就表示与这个SelectionKey对象相关的事件发生了。

ServerSocketChannel及SokcetChannel都是SelectableChannel的子类。SelectableChannel类及其子类都能委托Selector来监控它们可能发生的一些事件,这种为头过程也称为

为注册事件过程。

ServerSocketChannel向Selector注册接收连接就绪事件的代码如下:

SelectionKey key = serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);

SelectionKey 类的一些静态常量表示事件类型,ServerSocketChannel只可能发生一种事件。

SelectionKey.OP_ACCEPT:接收连接就绪事件,表示至少有了一个客户连接,服务器可以接收这个连接。

SokcetChannel可能发生以下3种事件。

SelectionKey.OP_CONNECT:连接就绪事件,表示客户与服务器的连接已经建立成功。

SelectionKey.OP_READ:读就绪事件,表示输入流中已经有了可读数据,可移植性读操作了。

SelectionKey.OP_WRITE:写就绪事件,表示已经可以向输出流写数据了。

SocketChannel提供了接收和发送数据的方法。

read(ByteBuffer buffer):接收数据,把它们存放到参数指定的ByteBuffer中。

write(ByteBuffer buffer):把参数指定的ByteBuffer中的数据发送出去。

ByteBuffer表示字节缓冲区,SocketChannel的read()和write()方法都会操纵ByteBuffer。ByteBuffer类继承于Buffer类。ByteBuffer中存放的是字节,为了把它们转换为字符串,还需要用到Charset类,Charset类代表字符编码,它提供了把字节流转换为字符串(解码过程)和把字符串转换为字节流(编码过程)的实用方法。

缓冲区Buffer

数据输入和输出往往是比较好事的操作。缓冲区从两个方面提高I/O操作的效率:

减少实际的物理读写次数;

缓冲区在创建时被分配内存,这块内存区域一直被重用,这可以减少动态分配和回收内存区域的次数。

所有的缓冲区都有以下属性。

容量:表示该缓冲区可以保存多少数据。

极限:表示缓冲区的当前终点,不能对缓冲区中超过极限的区域进行读写操作。极限是可以修改的,这有利于缓冲区的重用。极限是个非负数,不应该大于容量。

位置:表示缓冲区中下一个读写单元的位置,每次读写缓冲区的数据时,多会改变该值,为下一次读写数据做准备。位置是一个非负整数,不应该大于极限。

以上3个属性的关系为:容量>=极限>=位置>=0

缓冲区提供了用于改变以上3个属性的方法。

clear():把极限设为容量,再把位置设为0.

flip():把极限设为位置,再把位置设为0

rewind():不改变极限,把位置设为0.

Buffer类的remaining()方法返回缓冲区的剩余容量,取值等于极限-位置。Buffer类的compact()方法删除缓冲区内从0到当前位置的内容,然后把从当前位置到极限的内容复制到0到极限-位置的区域内,当前位置和极限的取值也做相应的变化。

ByteBuffer类并没有提供公开的构造方法,但是提供了两个获得ByteBuffer实例的静态工厂方法。

allocate(int capacity):返回一个ByteBuffer对象,参数capacity指定缓冲区的容量。

directAllocate(int capacity):返回一个ByteBuffer对象,参数capacity指定缓冲区的容量。该方法返回的缓冲区称为直接缓冲区,它与当前操作系统能够更好地耦合,因此能进一步提高I/O操作的速度。但分配直接缓冲区的系统开销很大,因此只有在缓冲区较大并且长期存在,或者需要经常重用时,才使用这种缓冲区。

所有具体缓冲区类都提供了读写缓冲区的方法。

get():相对读。从缓冲区的当前位置读取一个单元的数据,读完后把位置加1.

get(int index):绝对读。从参数index指定的位置读取一个单元的数据。

put():相对写。向缓冲区的当前位置写入一个单元的数据,写完后把位置加1.

put(int index):绝对写。向参数index指定的位置写入一个单元的数据。

Charset类提供了编码与解码的方法:

ByteBuffer encode(String str):对参数str指定的字符串进行编码,把得到的字节序列存放在一个ByteBuffer对象中,并将其返回。

ByteBuffer encode(CharBuffer cb):对参数cb指定的字符缓冲区中字符进行编码,把得到的字节序列存放到一个ByteBuffer对象中,并将其返回。

ByteBuffer decode(ByteBuffer bb):把参数bb指定的ByteBuffer中的字节序列进行解码,把得到的字符序列存放在一个CharBuffer对象中,并将其返回。

Charset类的静态forName(String encode)方法返回一个Charset对象,它代表参数encode指定的编码类型。

Charset类还有一个静态方法defaultCharset(),它返回代表本地平台的默认字符编码的Charset对象。

java.nio.channels.Channel接口只声明了两个方法

close():关闭通道。         isOpen():判断通道是否打开。

通道在创建时被打开,一旦关闭通道,就不能重新打开了。

Channel接口的两个最重要的子接口是ReadableByteChannel和WritableByteChannel。ReadableByteChannel接口声明了read(ByteBuffer dst)方法,该方法把数据源的数据读入参数指定的ByteBuffer缓冲区中。WritableByteChannel接口声明了write(ByteBuffer src)方法,该方法把参数指定的ByteBuffer缓冲区中的数据写到数据汇中。ByteChannel接口是一个便利接口,它扩展了ReadableByteChannel和WriteByteChannel接口,因而同时支持读写操作。

ScatteringByteChannel接口扩展了ReadableByteChannel接口,允许分散的读取数据。分散读取数据是指单个读取操作能填充多个缓冲区。ScatteringByteChannel接口声明了read(ByteBuffer[] dsts)方法,该方法把从数据源读取的数据依次填充到参数指定的ByteBuffer数组的各个ByteBuffer中。GatheringByteChannel接口扩展了WritableByteChannel,允许集中地写入数据。集中写入数据是指单个写操作能把多个缓冲区的数据写到数据汇。GatheringByteChannel接口声明了write(ByteBuffer[] srcs)方法,该方法依次把参数指定的ByteBuffer数组的每个ByteBuffer中的数据写入数据汇。分散读取和集中写数据能进一步提高输入和输出操作的速度。

FileChannel类是Channel接口的实现类,代表一个与文件相连的通道。该类实现了ByteChannel、ScatteringByteChannel和GatheringByteChannel接口,支持读操作、写操作、分散苏操作和集中写操作。FileChannel类没有提供公开的构造方法,因此客户程序不能用new语句来构造它的实例。不过,在FileInputStream、FileOutputStream和RandomAccessFile类中提供了getChannel()方法,该方法返回相应的FileChannel对象。

SelectableChannel类

SelectableChannel是一种支持阻塞I/O和非阻塞I/O的通道。在非阻塞模式下,读写数据不会阻塞,并且SelectbalChannel可以向Selector注册读就绪和写就绪等事件。

SelectableChannel的主要方法如下

public SelecatbleChannel configureBlocking(boolean block) throws IOException

当参数block为true时,表示把SelectableChannel设为阻塞模式;如果参数block为false,表示把SelectableChannel设为非阻塞模式。默认情况下,SelectableChannel采用阻塞模式。该方法返回SelectableChannel对象本身的引用,相当于“return this”

SelectableChannel的isBocking()方法判断SelectableChannel是否处于阻塞模式,如果返回true,表示处于阻塞模式,否则表示粗语非阻塞模式

public SelectionKey register(Selector sel,int ops)throws ClosedChannelException

public SelectionKey register(Selector sel,int ops,Object attachment)throws ClosedChannelException

register()方法返回一个SelectionKey对象,SelectionKey用来跟踪注册的事件。第二个register()方法还有一个Object类型的参数attachment,它用于为SelectionKey关联一个附件,当被注册事件发生后,需要处理该事件时,可以从SelectionKey中获得这个附件,该附件可用来包含与处理这个事件相关的信息。

ServerSocketChannel类

ServerSocketChannel从SelectableChannel中继承了configureBlocking()和register()方法。ServerSocketChannel是ServerSocket的替代类,也具有负责接收客户连接的accept()方法。ServerSocketChannel并没有public类型的构造方法,必须通过它的静态方法open()来创建ServerSocketChannel对象。每个ServerSocketChannel对象都与一个ServerSocket对象关联。

ServerSocketChannel的主要方法

public static ServerSocketChannel open()throws IOException

这是ServerSocketChannel类的静态工厂方法,它返回一个ServerSocketChannel对象。这个对象没有与任何本地端口绑定,并且处于阻塞模式。

public SocketChannel accept() throws IOException

类似于ServerSocket的accept()方法,用于接收客户的连接。如果ServerSockeChannel处于非阻塞模式,当没有客户连接时,该方法立即返回null;如果ServerSocketChannel处于阻塞模式,当没有客户连接时,它会一直阻塞下去,知道有客户连接就绪,或者IOExceptin。

该方法返回的SocketChannel对象处于阻塞模式,如果希望把它改为非阻塞模式,必须执行

socketChannel.configureBlocking(false)

public final intvalidOps()

返回ServerSocketChannel所能产生的事件,这个方法总是返回SelectionKey.OP_ACCEPT

public ServerSocket socket()

返回与ServerSocketChannel关联的ServerSocket对象。每个ServerScoketChannel对象都与一个ServerSocket对相关联。

SocketChannel类

SocketChannel没有public类型的构造方法,必须通过它的静态方法open()来创建SocketChannel对象。

SocketChannel的主要方法

public static SocketChannel open() throws IOException

public static SocketChannel open(SocketAddress remote) throws IOException

SocketChannel的静态工厂方法open()负责创建SockeChannel对象,第二个带参数的构造方法 还会建立与远程服务器的连接。在阻塞模式及非阻塞模式下,第二个open()方法有不同的行为,这与SocketChannel类的connect()方法类似。open()方法返回的SocketChannel对象处于阻塞模式,如果把它改为非阻塞模式,执行socketChannel.configureBlocking(false)

public final int validOps()

返回SocketChannel所能产生的事件,这个方法总返回以下值:

SelectionKey.OP_CONNECT|SelectionKey.OP_READ|SelectionKey.OP_WRITE

public Socket socket()

返回与这个SocketChannel关联的Socket对象。每个SocketChannel对象都与一个Socket对象关联。

public boolean isConnected()

判断底层Socket是否已经建立了远程连接

public boolean isConnectionPending()

判断是否正在进行远程连接。当远程连接操作已经开始,但还没有完成时,则返回true,否则返回false。当底层Socket还没有开始连接,或者已经连接成功时,该方法返回false。

public boolean connect(SocketAddress remote)throws IOException

使底层Socket建立远程连接。当SocketChannel处于非阻塞模式时,如果立即连接成功,该方法返回true,如果不能理解连接成功,该方法返回false,成灰过会儿必须通过调用finishConnect()方法来完成连接。当SocketChannel处于阻塞模式,如果立即连接成功,该方法返回true,如果不能立即连接成功,将进入阻塞状态,知道连接成功,或者出现I/O异常。

public boolean finishConnect()throws IOException

试图完成连接远程服务器的操作。在非阻塞模式下,建立连接从调用SocketChannel的connect()方法开始,到调用finishConnect()方法结束。如果finishConnect()方法顺利完成连接,或者在调用此方法之前连接已经建立,则finishiConnect()立即返回true。如果连接操作还没有完成,则立即返回false;如果连接操作中遇到异常而失败,则抛出相应的I/O异常。在阻塞模式下,如果连接操作还没有完成,则会进入阻塞状态,知道连接完成,或者出现I/O异常。

public int read(ByteBuffer dst)throws IOException

从Channel中读到若干字节,把它们存放到参数指定的ByteBuffer中。read()方法返回实际上读入的字节数,有可能为0.如果返回-1,表示读到了输入流的末尾。

public int write(ByteBuffer src)throws IOExcption

把参数src指定的ByteBuffer中的字节写到Channel中。write()方法返回实际上输出的字节数,有可能为0.

Selector类

一个Selector对象中包含3种类型的SelectionKey集合。

all-keys集合:当前所有向Selector注册的SelectionKey的集合,Selector的keys()方法返回该集合。

selected-keys集合:相关事件已经被Selector捕获的SelectionKey的集合。Selector的selectedKeys()方法返回该集合。

cancelled-keys集合:已经被取消的SelectionKey的集合。Selector没有提供访问这种集合的方法。

以上第二种和第三种集合都是第一种集合的子集。对于一个新建的Selector对象,它的上述集合都为空。对于一个新建的Selector对象,它的上述集合都为空。

Selector类的主要方法

public static Selector open() throws IOException

这是Selector的静态工厂方法,创建一个Selector对象。

public boolean  isOpen()

判断Selector是否处于打开状态。Selector对象创建后就处于打开状态,当调用了Selector对象的close()方法,它就进入关闭状态。

public Set<SelectionKey>keys()

返回Selector的all-keys集合,它包含了所有与Selector关联的SelectionKey对象。

public int selectNow()throws IOException

返回相关事件已经发生的SelectionKey对象的数目。该方法采用非阻塞的工作方式,返回当前相关事件已经发生的SelectionKey对象的数目,如果没有,就立即返回0.

public int select() throws IOException

public int select(long timeout)throws IOException

该方法采用阻塞的工作方式,返回相关事件已经发生的SelectionKey对象的数目,如果一个也没有,就进入阻塞状态,直到出现以下情况之一,才从select()方法返回。

至少一个SelectionKey的相关事件已经发生。

其他线程调用了Selector的wakeup()方法,导致执行select()方法的想成理解从select()方法中返回

当前执行select()方法的线程被其他线程中断

超出了等待时间。如果等待超时,就会正常返回,但不会抛出超时异常。

public Selector wakeup()

唤醒执行Selector的select()方法的线程。

public void close() throws IOExpression

关闭Selector。这个方法使得Selector占用所有资源都被释放,所有与Selector关联的SelectionKey都被取消。

SelectionKey类

在SelectionKey中定义了4种事件,分别用4个int类型的敞亮来表示

SelectionKey.OP_ACCEPT:接收就绪事件,表示服务器监听到了客户连接,服务器可以接收这个连接,常量值16

SelectionKey.OP_CONNECT:连接就绪事件,表示客户与服务器的连接已经建立成功。常量值8

SelectionKey.OP_READ:读就绪事件,表示通道中已经有了可读数据,可以执行读操作了。常量值1

SelectionKey.OP_WRITE:写就绪事件,表示已经可以向通道写数据了。常量值4

以上常量分别占据不同的二进制位,可以通过二进制的或运算“|”将它们任意组合。一个SelectionKey对象中包含两种类型的事件。

所有感兴趣的事件:SelectionKey的interestOps()方法返回所有感兴趣的事件。

SelectionKey的interestOps(int ops)方法用于为SelectionKey对象增加一个感兴趣的事件。

所有已经发生的事件:SelectionKey的readyOps()方法返回所有已经发生的事件

SelectionKey的主要方法

public SelectableChannel channel()

返回与这个SelectionKey对象关联的SelectableChannel对象

public Selector selector()

返回与这个SelectionKey对象关联的Selector对象

public boolean isValid()

判断这个SelectionKey是否有效。

public void cancel()

使SelectionKey对象失效。

public int interestOps()

返回这个SelectionKey感兴趣的事件

public SelectionKey interestOps(int ops)

为SelectionKey增加感兴趣的事件。

public int readOps()

返回已经就绪的事件。

public final boolean isReadable()

判断与之关联的SocketChannel的读就绪事件是否已经发生。

public final boolean isWritable()

判断与之关联的SocketChannel的写就绪事件是否已经发生。

public final boolean isConnectable()

判断与之关联的SocketChannel的连接就绪事件是否已经发生。

public final boolean isAcceptable()

判断与之关联的ServerSocketChannel的接收连接就绪事件是否已经发生。

public final Object attach(Object ob)

使SelectionKey关联一个附件。一个SelectionKey对象只能关联一个Object类型的附件。如果多次调用该方法,则只有最后一个附件与SelectionKey对象关联。

public final Object attachment()

返回与SelectionKey对象关联的附件


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值