一 . 什么是线层阻塞?
线程阻塞即线程高风亮节让出或放弃CPU,暂停执行,只有等到导致阻塞的原因解除,才能恢复运行;或者被其他线程中断,该线程会退出阻塞状态,并且抛出InterrutedException.
二 .常见的导致线程阻塞的原因:
Ø线程执行了Thread.sleep(intn)方法,线程放弃CPU,睡眠n毫秒,然后恢复运行。但是此时被该线程所控制的同步代码块的锁定释放权利依然属于该线程,其他线程依然不能访问该代码块。
Ø线程要执行一段同步代码,由于无法获得相关的同步锁,只好进入阻塞状态,等到获得了同步锁才能恢复运行。
Ø线程执行了一个对象的wait()方法,进入阻塞状态,只有等到其他线程执行了该对象的notify()或notifyAll()方法,才可将其唤醒。
Ø线程执行I/O操作或进行远程通信时,会因为等待相关的资源而进入阻塞状态。例如,当线程执行System.in.read()方法时,如果用户没有向控制台输入数据,则该线程会一直等到了用户的输入数据才才read()方法返回。
三 .什么是非阻塞
所谓非阻塞,就是指当线程执行某些方法时,如果操作还没有就绪,就立即返回,而不会一直等到操作就绪。
四 .什么是阻塞I/O和非阻塞I/O
可能出现阻塞的输入和输出操作被称为阻塞I/O;于此对照,如果执行输入和输出操作时,不会发生阻塞,则称为非阻塞I/O。
五 .Java.nio包提供了支持非阻塞通信的类
· ServerSocketChannel:ServerSocket的替代类,支持阻塞通信和非阻塞通信。
· SocketChannel: Socket的替代类,支持阻塞通信和非阻塞通信。
· Selector: 为ServerSocketChannel监控连接就绪事件,为SocketChannel监控连接就绪,读就绪和写就绪事件。
· SelectionKey: 代表ServerSocketChannel及SocketChannel注册事件的句柄。当一个SelectionKey对象位于Selector对象的selector-keys集合中时,就表示与这个SeletionKey对象相关的事件发生了。
· Charset类:代表字符编码,它提供了把字节流转换为字符串(解码过程)
和把字符串转换为字节流(编码过程)的使用方法。
· Buffer: 数据输入和输出往往是比较耗时的操作。缓冲区从两个方面提高I/O操作的效率:
§减少实际的物理读写次数;
§缓冲区在创建时被分配内存,这块内存区域一直被重用,这可以减少动态分配和回收内存区域的次数,可以通过修改缓冲区的极限属性来达到缓冲区重用的目的;
·缓冲区提供了三个属性来控制缓冲区的使用:容量,极限和位置,详细介绍参考java API文档;于此同时缓冲区还提供了用于改变以上3个属性的方法
Ø clear(): 把极限设置为容量的值,再把位置设为0
Ø flip(): 把极限设置为位置的值,再把位置设为0
Ø rewind():不改变极限的值,把位置设为0
其中,flip()方法为从Buffer中取数据做好了准备,而clear()则向Buffer中装入数据做好准备。
有关缓冲区的更加详细的介绍请参考java API文档。
·通道Channel用来连接缓冲区与数据源或数据汇(数据目的地)
通道创建时被打开,一旦关闭通道,就不能重新打开了
数据源 |
通道 |
缓冲区 |
通道 |
数据汇 |
·高级通道操作:提供了分散读取和集中写数据的类和相关方法,可以进一步的提高输入和输出操作的速度,详述请参考java API文档。
§具有自动增长的缓冲区的ChannelIO类
ChannelIO对SocketChannel进行了包装,增加了自动增长缓冲区容量的功能。当调用socketChannel.read(ByteBuffer buffer)方法时,如果buffer已满(position=limit)那么即使通道中还有未接收的数据,read方法也不会读取任何数据,二是直接返回0,表示读到了0个字节。
为了能读取通道中的所有数据,必须保证缓冲区的容量足够大。在ChannelIO类中,有一个requestBuffer变量,它用来存放客户的HTTP请求数据,当requestBuffer剩余容量已经不足5%,并且还有HTTP请求数据未接收时,ChannelIO会自动扩充requestBuffer的容量,该功能由resizeRequestBuffer()方法完成。
如下所示是ChannelIO类的源程序,它的read()和write()方法利用SocketChannel来接收和发送数据,并且它还提供了实用方法transferTo(),该方法能把文件中的数据发送到SockChannel中
//此处省略import语句
Public class ChannelIO{
Protected SocketChannelsocketChannel;
//存放请求数据
Protected ByteBuffer requestBuffer;
Private static int requestBufferSize= 4096;
Public ChannelIO() throwsIOException{
this.socketChannel =socketChannel;
//设置模式为阻塞模式或非阻塞模式
socketChannel.configureBlocking(blocking);
requestBuffer =ByteBuffer.allocate(requestBufferSize);
}
Public SocketChannel getSocketChannel(){
ReturnsocketChannel;
}
/**
*
*如果原缓冲区的剩余容量不够,就创建一个新的缓冲区,容量为原来的两倍,把原来缓冲区的数据复制到新缓冲区
*
/
Protected void resizeRequestBuffer(intremaining){
If(requestBuffer.remaining()< remianing){
//把容量增大到原来的两倍
ByteBufferbb = ByteBuffer.allocate(requestBuffer.capcity()*2);
//把极限设置为位置的值
requestBuffer.flip();
//把原来缓冲区中的数据复制到新的缓冲区
bb.put(requestBuffer);
requestBuffer= bb;
}
}
//接受数据,把它们存放到requestBuffer中,如果requestBuffer的剩余容量不足5%,就通过resizeRequestBuffer(int remaining)方法扩充容量
Public int read() throws IOException {
resizeRequestBuffer(requestBufferSize/20);
returnsocketChannel.read(requestBuffer);
}
//返回requestBuffer,它存放了请求数据
Public ByteBuffer getReadBuf(){
ReturnrequestBuffer;
}
//发送参数指定的ByteBuffer中的数据
Public int write(ByteBuffer src) throwsIOException {
ReturnsocketChannel.write(src);
}
//把FileChannel中的数据写到SocketChannel中
Public long transgerTo(FileChannel fc, longpos, long len) throws IOExcetion{
Returnfc.transferTo(pos,len,socketChannel);
}
//关闭SocketChannel
Public void close() throws IOException{
socketChannel.close();
}
}