1.5 多线程如何应用到实际场景

线程本质上是一个工具,它必须依托于相关的业务场景来使用。就像Java中的集合,我们会在涉及数据存储的地方用到。

而线程的使用场景,是根据线程异步和并行执行的特性来决定的,这些特性可以提升程序的处理性能,比如:

• 网络请求分发场景:针对每个请求连接,可以通过分配一个线程去处理,从而提升服务端处理的连接数,比如Tomcat就是采用线程来处理客户端请求的。

• 文件导入的场景:当有较大的文件导入时,我们可以先对文件进行解析,然后以每10000条数据作为一个任务给线程来处理,这样可以提升文件处理的效率。

• 异步业务场景:如支付场景中,客户端发起支付请求,服务端收到支付请求后,先不直接调用渠道进行支付,而是先返回给用户一个处理成功的结果,再通过异步线程的方式触发这个支付请求。

为了更好地理解线程的应用,笔者通过一个案例来演示线程在网络通信中的实际应用,该案例的整体流程如图1-4所示。

图1-4 线程在网络通信中的实际应用

在BIO(阻塞I/O)模型中,服务端基于ServerSocket.accept()方法来接收客户端的请求,当客户端发起一次请求后,在等待服务端返回执行结果前,ServerSocket无法处理其他请求。也就是说,服务端同一时刻只能处理一个请求,在多用户访问的系统中,这样显然会产生不好的用户体验。

如果使用多线程的方式来实现,那么服务端可以针对每个请求,分配一个专门的线程去处理,然后服务端继续接收下一个请求。通过这种方式的优化可以大大提升服务端同时处理的客户端请求数量,下文详细介绍具体的实现。

1.5.1 ServerSocket

ServerSocket的代码如下。

上述代码的请求处理逻辑说明如下。

• ServerSocket定义了一个监听端口8080,当客户端访问8080时,服务端就会接收请求。

• Server端接收请求是采用accept()方法来实现的,当收到一个客户端请求时,会返回一个Socket实例。

• 当Server端收到请求后,采用Thread异步执行该请求,由于线程具有非阻塞性,因此可以快速进入第二次循环,继续监听客户端请求。

1.5.2 SocketThread

SocketThread线程用来专门处理客户端发送过来的请求,代码如下。

在Tomcat 7中,针对请求的处理默认就是使用线程池改造后的BIO模式来实现的。在Zookeeper、Nacos、Dubbo等中间件中,也都大量采用了异步线程。