一、用单线程来优化不活跃线程较多的情况
BIO等待连接时需要阻塞,等待接收客户端发送数据时也需要阻塞。如果有一个客户端连接了服务端,但一直不发送消息,服务端就一直阻塞等待接收客户端的消息,无法接收其他客户端的连接。因此,如果不用多线程,BIO无法处理并发。
但加入多线程处理并发有一个弊端,如有1000个客户端来连服务端,服务端就需要开启1000个线程来处理客户端的数据,但1000个连接里只有100个连接是活跃连接,另外900个只连接不发送消息,那这900个线程就是浪费。因此,当服务器端不活跃的线程比较多时,要用单线程的方案来优化。
二、代码演示
2.1 IO客户端代码
import java.net.Socket;
import java.util.Scanner;
/**
* @author Alan Chen
* @description IO测试
* @date 2020/8/22
*/
public class Client {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("127.0.0.1", 8080);
//socket.getOutputStream().write("alanchen".getBytes());
// 体现服务端read是阻塞的,要等客户端发送数据
Scanner scanner = new Scanner(System.in);
System.out.println("请输入内容:");
while (true){
String next = scanner.next();
socket.getOutputStream().write(next.getBytes());
}
}
}
2.2 BIO 代码
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author Alan Chen
* @description BIO测试
* @date 2020/8/22
*/
public class BIOServer {
static byte[] bytes = new byte[1024];
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(8080));
while (true){
System.out.println("等待连接");
// 阻塞
Socket socket = serverSocket.accept();
System.out.println("连接成功");
/**
* 如果有客户端连接了,但一直不发送数据,服务端就会阻塞在这里,
* 无法在处理其他连接,如果要实现并发,则需要开启一个子线程来接收数据
* 线程代码:略
*/
System.out.println("等待接收数据");
//阻塞 读了多少字节
int read = socket.getInputStream().read(bytes);
System.out.println("数据接收成功");
String content = new String(bytes);
System.out.println(content);
}
}
}
2.3 NIO代码
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
/**
* @author Alan Chen
* @description NIO测试
* @date 2020/8/22
*/
public class NIOServer {
static List<SocketChannel> socketChannelList = new ArrayList<>();
static ByteBuffer byteBuffer = ByteBuffer.allocateDirect(512);
public static void main(String[] args) throws Exception {
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.bind(new InetSocketAddress(8080));
//设置为非阻塞
serverSocket.configureBlocking(false);
while (true){
SocketChannel socket = serverSocket.accept();
if(socket==null){
Thread.sleep(1500);
System.out.println("没有连接请求");
}else {
System.out.println("有连接请求");
//设置为非阻塞
socket.configureBlocking(false);
socketChannelList.add(socket);
}
/**
* 这种轮询的方式效率也很低,比如有1000万个连接,
* 但只有200万个是活跃的,每次都沦陷另外800万个连接,也是一种资源浪费,
* 解决方案是用epoll
*/
for(SocketChannel socketChannel : socketChannelList){
int k = socketChannel.read(byteBuffer);
if(k>0){
byteBuffer.flip();
System.out.println(Charset.forName("utf-8").decode(byteBuffer));
}
}
}
}
}
网友评论