码迷,mamicode.com
首页 > 其他好文 > 详细

阿里面试题BIO和NIO数量问题附答案和代码

时间:2020-08-02 10:14:31      阅读:73      评论:0      收藏:0      [点我收藏+]

标签:figure   puts   cat   nios   唤醒   直接   生产   关键点   int   

一、问题

BIO 和 NIO 作为 Server 端,当建立了 10 个连接时,分别产生多少个线程?

答案: 因为传统的 IO 也就是 BIO 是同步线程堵塞的,所以每个连接都要分配一个专用线程来处理请求,这样 10 个连接就会创建 10 个线程去处理。而 NIO 是一种同步非阻塞的 I/O 模型,它的核心技术是多路复用,可以使用一个链接上的不同通道来处理不同的请求,所以即使有 10 个连接,对于 NIO 来说,开启 1 个线程就够了。

二、BIO 代码实现

public

class

DemoServer

extends

Thread

{

private

ServerSocket
 serverSocket
;

public

int
 getPort
()

{

return
  serverSocket
.
getLocalPort
();

}

public

void
 run
()

{

try

{
            serverSocket 
=

new

ServerSocket
(
0
);

while

(
true
)

{

Socket
 socket 
=
 serverSocket
.
accept
();

RequestHandler
 requestHandler 
=

new

RequestHandler
(
socket
);
                requestHandler
.
start
();

}

}

catch

(
IOException
 e
)

{
            e
.
printStackTrace
();

}

finally

{

if

(
serverSocket 
!=

null
)

{

try

{
                    serverSocket
.
close
();

}

catch

(
IOException
 e
)

{
                    e
.
printStackTrace
();

}

}

}

}

public

static

void
 main
(
String
[]
 args
)

throws

IOException

{

DemoServer
 server 
=

new

DemoServer
();
        server
.
start
();

try

(
Socket
 client 
=

new

Socket
(
InetAddress
.
getLocalHost
(),
 server
.
getPort
()))

{

BufferedReader
 bufferedReader 
=

new

BufferedReader
(
new

InputStreamReader
(
client
.
getInputStream
()));
            bufferedReader
.
lines
().
forEach
(
s 
->

System
.
out
.
println
(
s
));

}

}

}
// 简化实现,不做读取,直接发送字符串
class

RequestHandler

extends

Thread

{

private

Socket
 socket
;

RequestHandler
(
Socket
 socket
)

{

this
.
socket 
=
 socket
;

}

@Override

public

void
 run
()

{

try

(
PrintWriter
 out 
=

new

PrintWriter
(
socket
.
getOutputStream
());)

{
            out
.
println
(
"Hello world!"
);
            out
.
flush
();

}

catch

(
Exception
 e
)

{
            e
.
printStackTrace
();

}

}

}
  • 服务器端启动 ServerSocket,端口 0 表示自动绑定一个空闲端口。
  • 调用 accept 方法,阻塞等待客户端连接。
  • 利用 Socket 模拟了一个简单的客户端,只进行连接、读取、打印。
  • 当连接建立后,启动一个单独线程负责回复客户端请求。
    这样,一个简单的 Socket 服务器就被实现出来了。

技术图片

(图片来源于杨晓峰)

三、NIO 代码实现

public

class

NIOServer

extends

Thread

{

public

void
 run
()

{

try

(
Selector
 selector 
=

Selector
.
open
();

ServerSocketChannel
 serverSocket 
=

ServerSocketChannel
.
open
();)

{
// 创建 Selector 和 Channel
            serverSocket
.
bind
(
new

InetSocketAddress
(
InetAddress
.
getLocalHost
(),

8888
));
            serverSocket
.
configureBlocking
(
false
);

// 注册到 Selector,并说明关注点
            serverSocket
.
register
(
selector
,

SelectionKey
.
OP_ACCEPT
);

while

(
true
)

{
                selector
.
select
();
// 阻塞等待就绪的 Channel,这是关键点之一

Set
<
SelectionKey
>
 selectedKeys 
=
 selector
.
selectedKeys
();

Iterator
<
SelectionKey
>
 iter 
=
 selectedKeys
.
iterator
();

while

(
iter
.
hasNext
())

{

SelectionKey
 key 
=
 iter
.
next
();

// 生产系统中一般会额外进行就绪状态检查
                    sayHelloWorld
((
ServerSocketChannel
)
 key
.
channel
());
                    iter
.
remove
();

}

}

}

catch

(
IOException
 e
)

{
            e
.
printStackTrace
();

}

}

private

void
 sayHelloWorld
(
ServerSocketChannel
 server
)

throws

IOException

{

try

(
SocketChannel
 client 
=
 server
.
accept
();)

{
          client
.
write
(
Charset
.
defaultCharset
().
encode
(
"Hello world!"
));

}

}

// 省略了与前面类似的 main
}
    • 首先,通过 Selector.open() 创建一个 Selector,作为类似调度员的角色。
    • 然后,创建一个 ServerSocketChannel,并且向 Selector 注册,通过指定 SelectionKey.OP_ACCEPT,告诉调度员,它关注的是新的连接请求。注意:为什么我们要明确配置非阻塞模式呢?这是因为阻塞模式下,注册操作是不允许的,会抛出 IllegalBlockingModeException 异常。
    • Selector 阻塞在 select 操作,当有 Channel 发生接入请求,就会被唤醒。
    • 在 sayHelloWorld 方法中,通过 SocketChannel 和 Buffer 进行数据操作,在本例中是发送了一段字符串。
      可以看到,在前面两个样例中,IO 都是同步阻塞模式,所以需要多线程以实现多任务处理。而 NIO 则是利用了单线程轮询事件的机制,通过高效地定位就绪的 Channel,来决定做什么,仅仅 select 阶段是阻塞的,可以有效避免大量客户端连接时,频繁线程切换带来的问题,应用的扩展能力有了非常大的提高。下面这张图对这种实现思路进行了形象地说明。

技术图片

(图片来源于杨晓峰)

四、参考资料

杨晓峰《Java核心36讲》
技术图片

近期热门文章:

Java 最常见的 200+ 面试题

阿里面试题BIO和NIO数量问题附答案和代码

标签:figure   puts   cat   nios   唤醒   直接   生产   关键点   int   

原文地址:https://blog.51cto.com/13904087/2515745

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!