参考: 广义同步&异步 阻塞&非阻塞 及 网络IO中的同步&异步,阻塞&非阻塞
从JDK1.4版本开始,引入了非阻塞的通信机制。
服务器程序接受客户连接,客户程序建立与服务器的连接,以及服务器程序和客户端程序收发数据的操作都可以按非阻塞的方式进行。服务器程序只需要创建一个线程,就能完成同时与多个客户通信的任务。
线程在运行中会因为某些原因而阻塞,所有处于阻塞状态的线程的共同特征是:
放弃CPU,暂停运行,只有等到导致阻塞的原因消除,才能恢复运行;或者被其他线程中断,该线程会退出阻塞状态,并且抛出InterruptedException。
导致线程阻塞的原因主要有以下几方面:
在进行远程通信时,在客户程序中,线程在以下情况可能进入阻塞状态:
在服务器程序中,线程在一下情况下可能会进入阻塞状态:
这种可能出现阻塞的输入和输出操作被称为阻塞I/O。与此对照,如果执行输入和输出操作时,不会发生阻塞,则称为非阻塞I/O。
Java虚拟机会为每个线程分配独立的堆栈空间,工作线程数目越多,系统开销就越大,而且增加了Java虚拟机调度线程的负担,增减了线程之间同步的复杂性,提高了线程死锁的可能性
工作线程的时间大多浪费在阻塞I/O操作上,Java虚拟机需要频繁的转让CPU的使用权,使进入阻塞状态的线程放弃CPU,在把CPU分配给处于可运行状态的线程。
保持适当的工作线程,会提高服务器的并发性能,但是当工作线程的数目达到某个极限,超出了系统的负荷,反而会降低并发性能,使得多数客户无法快速得到服务器的响应。
所谓非阻塞,就是指当线程执行这些方法时,如果操作还没有就绪,就立即返回,而不会一直等到操作就绪。
例如:当线程接受客户连接时,如果没有客户连接,就立即返回;或者当线程从输入流中读数据时,如果输入流中还没有数据,就立即返回;或者如果输入流中还没有足够的数据,那么就读取现有的数据,然后返回。
while (一直等待,直到有接受连接就绪事件,或读就绪事件,或写就绪事件发生时){ // 阻塞 if (有客户连接) 接收客户的连接; // 非阻塞 if (某个Socket的输入流中有可读数据) 从输入流中读数据; // 非阻塞 if (某个Socket的输出流可以写数据) 向输出流写数据; // 非阻塞 }
注意:上面while循环条件中的操作还是按照阻塞方式进行的,如果未发生任何事件,就会进入阻塞状态,直到接受连接就绪,读就绪事件或写就绪事件中至少有一个事件发生时,才会执行while循环体中的操作。在while循环中,一般会包含在特定条件下退出循环的操作。