你还在用单线程处理请求?这年头还不会写多线程服务器,真的不慌吗?

你好,欢迎来到我的博客!我是【菜鸟不学编程】
   我是一个正在奋斗中的职场码农,步入职场多年,正在从“小码农”慢慢成长为有深度、有思考的技术人。在这条不断进阶的路上,我决定记录下自己的学习与成长过程,也希望通过博客结识更多志同道合的朋友。
  
  ️ 主要方向包括 Java 基础、Spring 全家桶、数据库优化、项目实战等,也会分享一些踩坑经历与面试复盘,希望能为还在迷茫中的你提供一些参考。
   我相信:写作是一种思考的过程,分享是一种进步的方式。
  
   如果你和我一样热爱技术、热爱成长,欢迎关注我,一起交流进步!

全文目录:

    • ❓前言
    • 前置知识你得懂一点,不然真听不懂
    • 实战演示:手写一个多线程的Socket服务端
      • 1️⃣ 服务端代码(支持多客户端并发连接)
      • 2️⃣ 客户端代码(多个客户端你自己开多个就行)
    • 你看懂了吗?这里才是多线程的灵魂
      • 几个关键知识点:
    • 延伸一波,带你入点更深的坑
    • ✍️写在最后:代码你可以复制,理解必须原创!
    • 总结
    • 你还在用单线程服务?真的不考虑提升一下姿势水平?
    • 写在最后

❓前言

唉,说实话,现在做 Java 后端的朋友,如果你还没真正理解多线程服务器的实现机制,我真的是要摇摇头了。你以为 Tomcat 牛,是因为它 logo 好看?还不是背后的线程池机制 + 多路复用模型给力!

今天我不跟你扯那些晦涩难懂的理论,咱们实打实撸个Java 多线程 Socket 服务端实例,从最底层理解多线程服务器的运行机制,看完之后保证你不止知道“怎么用”,更能体会到“为什么这么用”。

前置知识你得懂一点,不然真听不懂

先别急着上代码,我们得先把下面几个概念捋清楚,要不然后面看代码你可能会发出“???”的声音:

  • Socket 编程:Java 网络编程最基础的玩意儿,就是一端监听,另一端请求连接,类似打电话。
  • 线程/多线程:你主线程一个人在干活,那肯定慢啊,多个线程同时响应多个客户端,这才是正解。
  • 阻塞/非阻塞模型:这跟你排队买奶茶一样,阻塞就是“等到买完了才能接下一个”,非阻塞是“多个窗口同时服务”。
  • 线程池(ExecutorService):别一个请求就 new 一个线程,GC 都能把你累死。线程池才是正道。

实战演示:手写一个多线程的Socket服务端

来,废话不多说,直接上干货!

1️⃣ 服务端代码(支持多客户端并发连接)

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();
                }
            }
        }
    }
}

2️⃣ 客户端代码(多个客户端你自己开多个就行)

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 然后丢到线程池嘛,这有啥难的?”

这可就是本事啊朋友。真正厉害的服务端不是你能不能“跑起来”,而是你得知道为啥用线程池、怎么防止线程泄露、怎么处理高并发

几个关键知识点:

  1. 为什么用线程池而不是直接 new Thread?
    因为频繁创建和销毁线程开销太大,还可能引发OOM(Out of Memory)。线程池能重用线程,而且可以限制最大并发线程数,避免服务器挂掉。

  2. 为什么要封装成 ClientHandler?
    解耦逻辑,让每个连接单独处理,逻辑清晰又好维护。你直接 inline 写代码以后哭的就是你。

  3. 如果客户端断开了怎么办?
    那就捕获异常呗,记得关闭 Socket 释放资源。这就是“防御式编程”。

延伸一波,带你入点更深的坑

这只是最基础的“阻塞式 BIO 多线程模型”。但在真实生产环境中你会发现:

  • 大量并发连接会拖死线程池
  • 阻塞 I/O 导致线程等待时间过长
  • 可扩展性差

于是就有了:

  • NIO(非阻塞 IO):引入 Selector,一个线程可以监听多个通道
  • Netty:基于 NIO 封装的异步事件驱动框架,性能炸裂
  • Reactor/Proactor 模型:更细粒度的事件驱动方式

你以为这篇文章结束了?不,我这是给你挖坑,下次就把 NIO 和 Netty 拿出来给你好好开堂大课

✍️写在最后:代码你可以复制,理解必须原创!

说真的啊,能坚持读到这里的,基本都是有点“技术洁癖”的人了,我敬你是条好汉。

别小看这几十行代码,背后是线程、IO、网络编程、设计模式等一大串知识点的融合。理解了它,你就不是那种“面试手撕一堆,入职啥都不会”的纸老虎。

总结

项目 内容
技术点 Socket、多线程、线程池
实战类型 多线程服务端 + 客户端通信
编程语言 Java
延伸方向 NIO、Netty、异步模型
推荐人群 Java 初中高级开发者

你还在用单线程服务?真的不考虑提升一下姿势水平?

如果你看完觉得有帮助,不妨点个赞或者收藏,甚至留言互怼(别客气,我顶得住)。下篇我来手把手写 Netty,让你彻底摆脱“Socket = 土味代码”的误区。

写在最后

如果你觉得这篇文章对你有帮助,或者有任何想法、建议,欢迎在评论区留言交流!你的每一个点赞 、收藏 ⭐、关注 ❤️,都是我持续更新的最大动力!

我是一个在代码世界里不断摸索的小码农,愿我们都能在成长的路上越走越远,越学越强!

感谢你的阅读,我们下篇文章再见~

✍️ 作者:某个被流“治愈”过的 Java 老兵
日期:2025-07-02
本文原创,转载请注明出处。

你可能感兴趣的:(Java从入门到放弃,java,开发语言)