java 阻塞 io_java BIO(阻塞IO,即传统IO)分析

本文探讨了Java传统BIO模型的不足之处,主要在于其阻塞式I/O导致的服务端线程资源浪费和效率低下。当客户端连接时,服务器线程会被accept方法阻塞,而在等待数据时,socket.getInputStream.read方法也会阻塞,这在高并发场景下尤为明显。因此,BIO适合处理少量客户端请求,不适合大规模并发。为解决这些问题,可以考虑使用NIO或异步非阻塞I/O模型提高服务器性能。
摘要由CSDN通过智能技术生成

对于java 传统的BIO来说,究竟存在哪些缺点呢?

首先需要理解的是,对于传统的java io来说,总体上是一个连接一个线程,都会说这样的服务器处理方式效率不高而且浪费资源,那么究竟是怎么回事儿呢?

源码地址:https://github.com/50mengzhu/learnIo

81d9b916aa3a50391bf2a5569b569ea9.png

解读一下BIO的流程——

首先由服务器端开启一个Socket监听固定端口,等待客户端连接

等到和客户端线程建立连接,从连接中的数据流中等待读取数据

客户端下线之后,服务器的线程随之终止

package com.dx.bio;

import static com.dx.io.NetConstants.NET_BUFFER;

import static com.dx.io.NetConstants.SERVER_PORT;

import java.io.IOException;

import java.io.InputStream;

import java.net.ServerSocket;

import java.net.Socket;

import java.nio.charset.StandardCharsets;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.LinkedBlockingDeque;

import java.util.concurrent.ThreadFactory;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

import com.google.common.util.concurrent.ThreadFactoryBuilder;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

/**

* Bio 的一个服务端。

*

* @author daixiao

*/

public class BioServer {

/** 日志记录对象。 */

private static Log log = LogFactory.getLog(BioServer.class);

public static void main(final String[] args) {

// 创建一个线程池

ThreadFactory factory = new ThreadFactoryBuilder()

.setNameFormat("nio-pool-test-%d").build();

ExecutorService pool = new ThreadPoolExecutor(5, 200,

0L, TimeUnit.MILLISECONDS,

new LinkedBlockingDeque(NET_BUFFER), factory);

// 创建一个线程响应客户端

try (ServerSocket serverSocket = new ServerSocket(SERVER_PORT)) {

log.info(String.format("server is listening on port %d", SERVER_PORT));

while (true) {

// 针对进来连接的客户端进行处理

final Socket socket = serverSocket.accept();

log.info(String.format("a client connect to server: %s:%d",

socket.getInetAddress(), socket.getLocalPort()));

pool.execute(new Runnable() {

@Override

public void run() {

handleMsg(socket);

}

});

}

} catch (IOException e) {

log.warn("create server failed OR accept client failed", e);

}

}

/**

* 处理客户端信息的方法

*

* @param socket 客户端的 socket

*/

public static void handleMsg(Socket socket) {

log.info(String.format("current pid is %s, and thread name is %s",

Thread.currentThread().getId(), Thread.currentThread().getName()));

byte[] buffer = new byte[NET_BUFFER];

try (InputStream inputStream = socket.getInputStream()) {

while (true) {

if (inputStream.read(buffer) != -1) {

log.info(new String(buffer, 0, buffer.length, StandardCharsets.UTF_8));

} else {

break;

}

}

} catch (IOException e) {

log.warn(String.format("require input stream from client %s:%d failed!",

socket.getInetAddress(), socket.getLocalPort()), e);

} finally {

log.info(String.format("close client %s:%d socket!",

socket.getInetAddress(), socket.getLocalPort()));

try {

socket.close();

} catch (IOException e) {

log.warn(String.format("close client %s:%d socket failed!",

socket.getInetAddress(), socket.getLocalPort()), e);

}

}

}

}

代码中的实现——首先是由服务器端创建一个ServerSocket等待客户端连接(注意此时就存在一个等待也就是serverSocket.accpet这个方法会一直阻塞等待客户端线程的连接),和客户端建立连接之后服务器端就会等待客户端的数据(此时是socket.getInputStream.read这个方法会一直阻塞等待客户端线程传送数据)。

/**

* Listens for a connection to be made to this socket and accepts

* it. The method blocks until a connection is made.

*

*

A new Socket {@code s} is created and, if there

* is a security manager,

* the security manager's {@code checkAccept} method is called

* with {@code s.getInetAddress().getHostAddress()} and

* {@code s.getPort()}

* as its arguments to ensure the operation is allowed.

* This could result in a SecurityException.

*

* @exception IOException if an I/O error occurs when waiting for a

* connection.

* @exception SecurityException if a security manager exists and its

* {@code checkAccept} method doesn't allow the operation.

* @exception SocketTimeoutException if a timeout was previously set with setSoTimeout and

* the timeout has been reached.

* @exception java.nio.channels.IllegalBlockingModeException

* if this socket has an associated channel, the channel is in

* non-blocking mode, and there is no connection ready to be

* accepted

*

* @return the new Socket

* @see SecurityManager#checkAccept

* @revised 1.4

* @spec JSR-51

*/

public Socket accept() throws IOException {

if (isClosed())

throw new SocketException("Socket is closed");

if (!isBound())

throw new SocketException("Socket is not bound yet");

Socket s = new Socket((SocketImpl) null);

implAccept(s);

return s;

}

查看源码中accept的注释——The method blocks until a connection is made,也就是说如果没有连接那么这个方法将会一直阻塞在这里。还有一个阻塞的地方——

/**

* Reads some number of bytes from the input stream and stores them into

* the buffer array b. The number of bytes actually read is

* returned as an integer. This method blocks until input data is

* available, end of file is detected, or an exception is thrown.

*

*

If the length of b is zero, then no bytes are read and

* 0 is returned; otherwise, there is an attempt to read at

* least one byte. If no byte is available because the stream is at the

* end of the file, the value -1 is returned; otherwise, at

* least one byte is read and stored into b.

*

*

The first byte read is stored into element b[0], the

* next one into b[1], and so on. The number of bytes read is,

* at most, equal to the length of b. Let k be the

* number of bytes actually read; these bytes will be stored in elements

* b[0] through b[k-1],

* leaving elements b[k] through

* b[b.length-1] unaffected.

*

*

The read(b) method for class InputStream

* has the same effect as:

 read(b, 0, b.length) 

*

* @param b the buffer into which the data is read.

* @return the total number of bytes read into the buffer, or

* -1 if there is no more data because the end of

* the stream has been reached.

* @exception IOException If the first byte cannot be read for any reason

* other than the end of the file, if the input stream has been closed, or

* if some other I/O error occurs.

* @exception NullPointerException if b is null.

* @see java.io.InputStream#read(byte[], int, int)

*/

public int read(byte b[]) throws IOException {

return read(b, 0, b.length);

}

在服务端的线程中存在以下几个问题——

服务端线程在读取数据的时候,如果流中目前不存在数据,那么服务端处理线程将会空等

操作系统切换线程也需要耗费大量的时间

在进行网络数据传输的时候,采用的是流的方式,效率不高

因此基于以上的两个原因,传统的IO才会对服务器造成很大的压力,适合处理请求数量较小的客户端请求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值