你好,欢迎来到我的博客!我是【菜鸟不学编程】
我是一个正在奋斗中的职场码农,步入职场多年,正在从“小码农”慢慢成长为有深度、有思考的技术人。在这条不断进阶的路上,我决定记录下自己的学习与成长过程,也希望通过博客结识更多志同道合的朋友。
️ 主要方向包括 Java 基础、Spring 全家桶、数据库优化、项目实战等,也会分享一些踩坑经历与面试复盘,希望能为还在迷茫中的你提供一些参考。
我相信:写作是一种思考的过程,分享是一种进步的方式。
如果你和我一样热爱技术、热爱成长,欢迎关注我,一起交流进步!
唉,说实话,现在做 Java 后端的朋友,如果你还没真正理解多线程服务器的实现机制,我真的是要摇摇头了。你以为 Tomcat 牛,是因为它 logo 好看?还不是背后的线程池机制 + 多路复用模型给力!
今天我不跟你扯那些晦涩难懂的理论,咱们实打实撸个Java 多线程 Socket 服务端实例,从最底层理解多线程服务器的运行机制,看完之后保证你不止知道“怎么用”,更能体会到“为什么这么用”。
先别急着上代码,我们得先把下面几个概念捋清楚,要不然后面看代码你可能会发出“???”的声音:
来,废话不多说,直接上干货!
import java.io.*;
import java.net.*;
import java.util.concurrent.*;
public class MultiThreadedServer {
// 线程池处理客户端连接
private static ExecutorService threadPool = Executors.newFixedThreadPool(10);
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println(" 服务器启动,端口8888,准备接客...");
while (true) {
// 阻塞等待客户端连接
Socket clientSocket = serverSocket.accept();
System.out.println(" 有客户端连接啦:" + clientSocket.getInetAddress());
// 每个连接交给线程池处理
threadPool.execute(new ClientHandler(clientSocket));
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 客户端处理类
static class ClientHandler implements Runnable {
private Socket socket;
public ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
) {
String message;
while ((message = reader.readLine()) != null) {
System.out.println(" 收到客户端消息:" + message);
writer.write("收到啦,别急!\n");
writer.flush();
}
} catch (IOException e) {
System.out.println("⚠️ 客户端连接异常:" + e.getMessage());
} finally {
try {
socket.close();
System.out.println(" 客户端断开连接");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
import java.io.*;
import java.net.*;
public class SimpleClient {
public static void main(String[] args) {
try (
Socket socket = new Socket("127.0.0.1", 8888);
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
) {
String input;
while ((input = console.readLine()) != null) {
writer.write(input + "\n");
writer.flush();
String response = reader.readLine();
System.out.println(" 服务器回复:" + response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
可能你会说,“不就是接个 Socket 然后丢到线程池嘛,这有啥难的?”
这可就是本事啊朋友。真正厉害的服务端不是你能不能“跑起来”,而是你得知道为啥用线程池、怎么防止线程泄露、怎么处理高并发。
为什么用线程池而不是直接 new Thread?
因为频繁创建和销毁线程开销太大,还可能引发OOM(Out of Memory)。线程池能重用线程,而且可以限制最大并发线程数,避免服务器挂掉。
为什么要封装成 ClientHandler?
解耦逻辑,让每个连接单独处理,逻辑清晰又好维护。你直接 inline 写代码以后哭的就是你。
如果客户端断开了怎么办?
那就捕获异常呗,记得关闭 Socket 释放资源。这就是“防御式编程”。
这只是最基础的“阻塞式 BIO 多线程模型”。但在真实生产环境中你会发现:
于是就有了:
你以为这篇文章结束了?不,我这是给你挖坑,下次就把 NIO 和 Netty 拿出来给你好好开堂大课
说真的啊,能坚持读到这里的,基本都是有点“技术洁癖”的人了,我敬你是条好汉。
别小看这几十行代码,背后是线程、IO、网络编程、设计模式等一大串知识点的融合。理解了它,你就不是那种“面试手撕一堆,入职啥都不会”的纸老虎。
项目 | 内容 |
---|---|
技术点 | Socket、多线程、线程池 |
实战类型 | 多线程服务端 + 客户端通信 |
编程语言 | Java |
延伸方向 | NIO、Netty、异步模型 |
推荐人群 | Java 初中高级开发者 |
如果你看完觉得有帮助,不妨点个赞或者收藏,甚至留言互怼(别客气,我顶得住)。下篇我来手把手写 Netty,让你彻底摆脱“Socket = 土味代码”的误区。
如果你觉得这篇文章对你有帮助,或者有任何想法、建议,欢迎在评论区留言交流!你的每一个点赞 、收藏 ⭐、关注 ❤️,都是我持续更新的最大动力!
我是一个在代码世界里不断摸索的小码农,愿我们都能在成长的路上越走越远,越学越强!
感谢你的阅读,我们下篇文章再见~
✍️ 作者:某个被流“治愈”过的 Java 老兵
日期:2025-07-02
本文原创,转载请注明出处。