生产者消费者模型丐中丐实现!!!

目录

引言

在面试中,多线程编程愈发常见,生产者-消费者模型是一种经典的线程通信模式,广泛应用于任务调度、消息处理、异步执行等场景。本文将通过一段实战代码示例,深入剖析如何使用 Java 的并发工具构建一个高并发安全、结构清晰的任务调度框架。

本文将围绕以下核心问题展开:

  • 如何控制生产和消费的节奏?
  • 如何保证任务数量不多不少、恰好执行完?
  • 如何优雅处理线程终止问题?
  • 如何进一步优化提升系统吞吐量?

代码结构总览

import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

public class Main {
    private static final int QUEUE_SIZE = 12;
    private static final int NUM_PRODUCER = 10;
    private static final int NUM_CONSUMER = 8;
    private static final int NUM_TASK = 100;

    private static BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(QUEUE_SIZE);
    private static AtomicInteger count1 = new AtomicInteger(0);
    private static AtomicInteger count2 = new AtomicInteger(0);

    public static void main(String[] args) {
        Thread[] producers = new Thread[NUM_PRODUCER];
        Thread[] consumers = new Thread[NUM_CONSUMER];
        for (int i = 0; i < NUM_PRODUCER; i++) {
            producers[i] = new Thread(new Producer(i));
            producers[i].start();
        }
        for (int i = 0; i < NUM_CONSUMER; i++) {
            consumers[i] = new Thread(new Consumer(i));
            consumers[i].start();
        }

        try {
            for (Thread p : producers) p.join();
            for (Thread c : consumers) c.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("ALL TASK HAS DONE.");
    }

    static class Producer implements Runnable {
        final int id;

        public Producer(int id) {
            this.id = id;
        }

        public void run() {
            while (true) {
                if (count1.get() >= NUM_TASK) break;
                try {
                    int curId = count1.incrementAndGet();
                    queue.put(curId);
                    System.out.println("P " + id + " HAS PRODUCED TASK " + curId);
                    Thread.sleep((int) (Math.random() * 2000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class Consumer implements Runnable {
        final int id;

        public Consumer(int id) {
            this.id = id;
        }

        public void run() {
            while (true) {
                if (count2.get() >= NUM_TASK) break;
                try {
                    Integer curId = queue.poll();
                    if (curId == null) {
                        Thread.sleep(10);
                        continue;
                    }
                    count2.incrementAndGet();
                    System.out.println("C " + id + " HAS CON           SUMED TASK " + curId);
                    Thread.sleep((int) (Math.random() * 2000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

这段代码实现了一个典型的生产者消费者模型:

  • 队列容量: 12
  • 生产者线程数: 10 个
  • 消费者线程数: 8 个
  • 总任务数: 100 个

关键成员包括:

  • BlockingQueue<Integer> queue:用于存储任务的共享队列;
  • AtomicInteger count1, count2:分别用于统计已生产与已消费的任务数,保证线程安全;
  • ProducerConsumer 实现了 Runnable 接口,分别处理任务的生成与消费。

核心代码讲解

共享资源

    private static BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(QUEUE_SIZE);
    private static AtomicInteger count1 = new AtomicInteger(0);
    private static AtomicInteger count2 = new AtomicInteger(0);

使用 LinkedBlockingQueue 实现线程安全的任务传递;

AtomicInteger 替代 synchronized,保证原子性并避免锁竞争。

生产者逻辑

    while (true) {
        if (count1.get() >= NUM_TASK) break;
        try {
            int curId = count1.incrementAndGet();
            queue.put(curId);
            System.out.println("P " + id + " HAS PRODUCED TASK " + curId);
            Thread.sleep((int) (Math.random() * 2000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

每个生产者线程不断尝试生成任务,直到总任务数达到上限。

queue.put(curId) 为阻塞操作,当队列满时自动等待。

注意

使用 incrementAndGet() 而非 getAndIncrement() 是为了防止出现越界任务。

任务编号可能不连续(由于并发 + 条件判断的竞态),但总量可控。

消费者逻辑

    while (true) {
        if (count2.get() >= NUM_TASK) break;
        try {
            Integer curId = queue.poll();
            if (curId == null) {
                Thread.sleep(10);
                continue;
            }
            count2.incrementAndGet();
            System.out.println("C " + id + " HAS CONSUMED TASK " + curId);
            Thread.sleep((int) (Math.random() * 2000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

消费者采用 poll() 非阻塞拉取,空则等待一段时间重试,确保所有消费者线程不阻塞;

成功获取任务后,立即执行并统计。

posted @ 2025-04-19 20:02  anyj1024  阅读(30)  评论(0)    收藏  举报