使用锁与Condition模拟打印服务器的实现需求咨询
Alright, let's build this thread-safe print server step by step. I'll use Java for the example—its concurrency APIs make this problem really straightforward, but I'll also cover a manual sync approach if you want to understand the underlying mechanics.
核心组件 & 思路
Let's break down what we need:
- Client Threads: Call
printRequestV1to submit messages—this method only adds the message to a shared queue, no printing logic here. - Manager Thread: Runs independently, pulls messages from the queue, and prints them to the screen. It needs to wait when the queue is empty instead of spinning uselessly.
- Synchronization: We need to make sure multiple client threads don't corrupt the queue, and the manager thread doesn't try to read from an empty queue.
方案1:用BlockingQueue(推荐,简洁可靠)
Java's BlockingQueue handles all the synchronization and wait/notify logic out of the box. LinkedBlockingQueue is a great choice here—it's thread-safe, and its put()/take() methods block automatically when the queue is full/empty.
Here's the full code:
import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class PrintServer { // 用无界队列,也可以指定容量比如new LinkedBlockingQueue<>(100) private final BlockingQueue<String> printQueue = new LinkedBlockingQueue<>(); private final Thread managerThread; public PrintServer() { // 初始化并启动管理线程 managerThread = new Thread(this::processPrintJobs); managerThread.start(); } // 客户端调用的打印请求方法 public void printRequestV1(String message) throws InterruptedException { // 把消息放入队列,队列满时会阻塞(如果是有界队列) printQueue.put(message); } // 管理线程的核心逻辑:持续处理打印作业 private void processPrintJobs() { try { while (!Thread.currentThread().isInterrupted()) { // 从队列取消息,队列为空时自动阻塞等待 String job = printQueue.take(); System.out.println("Printing: " + job); // 模拟打印耗时(可选) Thread.sleep(500); } } catch (InterruptedException e) { // 线程被中断,优雅退出 Thread.currentThread().interrupt(); System.out.println("Print server manager thread stopped."); } } // 关闭打印服务器的方法(可选) public void shutdown() { managerThread.interrupt(); } }
测试代码(模拟多客户端)
public class PrintServerTest { public static void main(String[] args) throws InterruptedException { PrintServer server = new PrintServer(); // 启动5个客户端线程提交打印请求 for (int i = 0; i < 5; i++) { int clientId = i + 1; new Thread(() -> { try { for (int j = 0; j < 3; j++) { String message = "Client " + clientId + " - Job " + (j + 1); server.printRequestV1(message); System.out.println("Client " + clientId + " submitted: " + message); Thread.sleep(200); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }).start(); } // 运行一段时间后关闭服务器 Thread.sleep(5000); server.shutdown(); } }
关键细节说明:
printQueue.take(): Automatically blocks when the queue is empty—no need to manually check if the queue is non-empty or handle wait/notify.printQueue.put(): Thread-safe, ensures multiple clients don't mess up the queue structure.- The manager thread runs in an infinite loop until interrupted, which lets us shut down the server gracefully.
方案2:手动实现同步(理解底层原理)
If you want to write the synchronization logic yourself (instead of relying on BlockingQueue), here's how to do it with synchronized, wait(), and notify():
import java.util.LinkedList; import java.util.Queue; public class ManualSyncPrintServer { private final Queue<String> printQueue = new LinkedList<>(); private final Thread managerThread; public ManualSyncPrintServer() { managerThread = new Thread(this::processPrintJobs); managerThread.start(); } public void printRequestV1(String message) { synchronized (printQueue) { printQueue.add(message); // 通知管理线程:有新作业了 printQueue.notify(); } } private void processPrintJobs() { try { while (!Thread.currentThread().isInterrupted()) { String job; synchronized (printQueue) { // 用while循环处理虚假唤醒,确保队列非空才继续 while (printQueue.isEmpty()) { printQueue.wait(); } job = printQueue.poll(); } System.out.println("Printing: " + job); Thread.sleep(500); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.out.println("Print server manager thread stopped."); } } public void shutdown() { managerThread.interrupt(); // 唤醒等待的线程,避免死锁 synchronized (printQueue) { printQueue.notify(); } } }
关键细节说明:
- Always use
whilechecks forwait()to handle spurious wakeups (threads can wake up without being notified). - Wrap all queue operations in
synchronizedblocks using the queue as the lock object. - Call
notify()after adding a job to wake up the manager thread (which might be waiting on an empty queue).
内容的提问来源于stack exchange,提问作者Pape Traore

