new Socket(ip, port)getInputStream()/getOutputStream()BufferedReader/PrintWriter 便利文本處理ServerSocket server = new ServerSocket(port);
while (true) {
Socket client = server.accept(); // 阻塞等待
handleConnection(client); // 完整處理才能接受下一個
}
缺點:一次只能服務一個客戶端,handleConnection 耗時 (如大檔傳輸) 會導致後續連線排隊甚至超時。
while (true) {
Socket client = server.accept();
new Thread(new ConnectionHandler(client)).start(); // 每連線一線程
}
ConnectionHandler implements Runnable,將原 handleConnection 邏輯移入 run()。new ServerSocket(port, backlog),backlog 指定作業系統接收隊列長度 (預設 50)。超過隊列的新連線會被拒絕 (Connection refused)。核心思想:啟動固定數量的 Handler 線程 (如 3 個),共享一個 任務隊列 (LinkedList)。主線程 accept() 後將 Socket 放入隊列並 notifyAll();Handler 線程 wait() 取任務處理。
// 伺服器主類
class PooledRemoteFileServer {
void setUpHandlers() {
for (int i=0; i pool = new LinkedList<>();
Socket connection;
static void processRequest(Socket s) {
synchronized (pool) {
pool.add(s);
pool.notifyAll(); // 喚醒等待的 Handler
}
}
public void run() {
while (true) {
synchronized (pool) {
while (pool.isEmpty())
pool.wait(); // 隊列空則等待
connection = pool.remove(0); // 取任務
}
handleConnection(); // 處理實際業務
}
}
}
synchronized(pool) 保護共享隊列的複合操作 (檢查+取出/加入+通知) 原子性。pool.wait() 會釋放鎖,讓 processRequest 能獲得鎖加入任務;被喚醒後重新獲取鎖繼續執行。notifyAll() 喚醒所有等待線程,但只有一個能搶到任務 (隊列移除),其餘重新進入 wait()。這稱為「驚群效應」,高併發下可改用 notify() 或 ReentrantLock + Condition 優化。| 模型 | 併發能力 | 資源控制 | 實作複雜度 | 適用場景 |
|---|---|---|---|---|
| 單執行緒 | 極低 | 無 | 最低 | 教學、極低負載 |
| Thread-per-Connection | 中 (受限於線程數) | 無 (線程無上限) | 低 | 中低負載、短連線 |
| 連線池 (固定線程+隊列) | 高 (可配置) | 精確控制線程/隊列上限 | 中 | 生產環境、長連線、高併發 |
手寫連線池教學意義大於實戰。實務請採用:
BlockingQueue 即可實現生產級連線池。定義 ClientSocketFacade/ServerSocketFacade 介面,上層業務邏輯僅依賴 Facade,底層可隨時替換為阻塞 IO、NIO、Netty 等實現,符合依賴倒置原則。
從 ServerSocket.accept() 到固定線程池+共享隊列,我們一步步解決了「一次只能服務一個」、「線程無限增長」、「頻繁建立銷毀」三大痛點。理解這條演進路徑,才能真正駕馭 Netty 等高階框架的設計哲學。Java 網路編程,核心永遠是「用優秀的 OO 設計封裝底層位元組細節,讓業務邏輯純粹、可測試、可替換」。