NIO学习笔记——解决“服务器端cpu占用率高”

问题:服务器端启动后,cpu占用率很高,经常是99%,100%。
原因分析:
RecvThread的终止判断条件最初是if (num == -1)//这里的num是指通道读取到buffer中的字节数,当没有数据时,客户端的IntputStream始终没有终止或关闭,
也就是说SocketChannel始终无法读到流的末尾。但是服务器的channel始终在尝试读取客户端的数据,但读取的数据都为空,
这样就使得cpu一直在做“无用功”——空转。

--------------------------------------------------
或许你会问,只不过才一个线程在接收数据而已,不至于使得cpu慢的如老牛耕地吧?你这样说是没错的。但是接下来,我在无意中做了个
小小的实验:我在RecvThread类中加入了运行时的debug信息。
另外,请观众注意,当我们在ListenThread中启动RecvThread并运行的同时,原先的线程ListenThread也没闲着,而是在继续往下执行。

服务器端启动后,我使用客户端发送了一个字符(仅仅是一个字符)给服务器端,意外出现了:服务器的console中打印了不止一个RecvThread线程的debug信息。
为什么会有这么多线程?按照我们的思路,当判断SelectionKey是readable厚,我们只new了一个线程来接收数据啊。而且NIO api中对

Selector的select()方法解释如下:

是啊,没错啊,只有select至少一个有效的通道时,select()才会返回,否则就一直阻塞。
但是,从我刚才的实验中,大家也许很清楚的看到了,其实当RecvThread处理数据的同时,select()方法并没有阻塞,继而它后面的程序仍然会继续执行。
以至于new 了第二个、第三个....第n个RecvThread的实例。
后来我专门又输出了select()的返回值,结果也进一步验证了我刚才所言——RecvThread线程处理数据的同时,select()并没有老老实实的阻塞在那里,而是返回了一个0。

最后,唯一令我感到欣慰的是,这n个线程并不是每一个都接收了客户端发来的数据,而是仅仅其中一个线程接收到了数据。

解决方法:把RecvThread的终止判断条件改成if (num <= 0),并且强烈建议您:不要在selectionKey.isReadable()判断之后,去新建一个线程来接收到来的数据。原因1是因为刚才我所讲的一个消息对应n个线程,将耗费掉大量的cpu资源,
而且这n个线程中为我们做事的却只有其中一个(这也许刚好就是当今经济危机下大部分企业都在裁员的原因)。原因之二,千万不要单纯以为这不过是n个线程而已,
服务器接收客户端消息是很频繁的,一个消息对应n个线程,那么100条消息就对应了.....我想你不会希望看到自己的cpu被这n*100个线程累垮。^_^

你可能感兴趣的:(多线程)