You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用锁与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 printRequestV1 to 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 while checks for wait() to handle spurious wakeups (threads can wake up without being notified).
  • Wrap all queue operations in synchronized blocks 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:53:32