2.1 IO编程
我们简化一下场景:客户端每隔两秒发送一个带有时间戳的“hello world”给服务端,服务端收到之后打印它。
为了方便演示,在下面的例子中,服务端和客户端各有一个类,把这两个类复制到你的IDE中,先后运行IOServer.java和IOClient.java,可以看到效果。
下面是传统的IO编程中的服务端实现。
IOServer.java
服务端首先创建一个serverSocket来监听8000端口,然后创建一个线程,线程里不断调用阻塞方法serverSocket.accept()获取新连接,见(1);当获得新连接之后,为每一个新连接都创建一个新线程,这个线程负责从该连接中读取数据,见(2);然后以字节流方式读取数据,见(3)。
下面是传统的IO编程中的客户端实现。
IOClient.java
客户端的代码相对简单,连接上服务端8000端口之后,每隔两秒,我们都向服务端写一个带有时间戳的“hello world”。
IO编程模型在客户端较少的情况下运行良好,但是对于客户端比较多的业务来说,单机服务端可能需要支撑成千上万个连接,IO模型可能就不太合适了,我们来分析一下原因。
在上面的示例中,从服务端代码可以看到,在传统的IO模型中,每个连接创建成功之后都需要由一个线程来维护,每个线程都包含一个while死循环,那么1万个连接对应1万个线程,继而有1万个while死循环,这就带来如下几个问题。
1.线程资源受限:线程是操作系统中非常宝贵的资源,同一时刻有大量的线程处于阻塞状态,是非常严重的资源浪费,操作系统耗不起。
2.线程切换效率低下:单机CPU核数固定,线程爆炸之后操作系统频繁进行线程切换,应用性能急剧下降。
3.除了以上两个问题,在IO编程中,我们看到数据读写是以字节流为单位的。
为了解决这3个问题,JDK在1.4版本之后提出了NIO。