线程同步之生产者-消费者模式Java代码实现

本文介绍了使用LinkedList和synchronized关键字以及BlockingQueue实现生产者-消费者模型。在第一个实现中,生产者和消费者线程通过自定义的消息队列交互,当队列满或空时,线程会进行等待和唤醒操作。第二个实现则利用阻塞队列LinkedBlockingQueue,简化了同步处理,线程在队列满或空时会自动阻塞。两种方法都通过ExecutorService创建线程池来模拟多个生产者和消费者。

生产者-消费者问题


生产者-消费者模式

  1. 生产者:只关心消息队列满没满,如果消息队列满了,就阻塞等待消费者take()操作,否则生产者进行put()操作往队列中添加消息,消息队列的容量capacity - 1。
  2. 消费者:只关心消息队列有没有消息,如果消息队列没有有,就阻塞等待生产者put()操作,否则消费者进行take()操作往队列中取出消息,消息队列的容量capacity + 1。

消费队列可以用来平衡生产和消费的线程资源。
在这里插入图片描述
实现方式:

  1. 使用LinkedList和synchronized实现消息队列存放消息。
  2. 使用Executors.newFixedThreadPool中的创建线程池的方法模拟生产者和消费者线程。
class MessageQueue<V> {
    private LinkedList<V> queue = new LinkedList<>();
    private int capacity;

    public MessageQueue(int capacity) {
        this.capacity = capacity;
    }

    public V get() {
        synchronized (this) {
            //队列为空,消费者需要等待
            String currName = Thread.currentThread().getName();
            while (queue.isEmpty()) {
                System.out.println(currName + " 队列为空,消费者线程等待。。。。");
                //暂停毫秒
                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    queue.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            //队列不空,消费者消费消息
            V message = queue.removeFirst();
            //打印消费的信息
            System.out.println(currName + " 消费了消息:" + message.toString());
            //唤醒其他阻塞线程
            queue.notifyAll();
            return message;
        }
    }

    public void put(V msg) {

        synchronized (this) {
            while (true) {
                //队列已满,生产者不能放入消息
                while (queue.size() == capacity) {
                    System.out.println(Thread.currentThread().getName() + " 队列已满,生产者线程等待");
                    try {
                        queue.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                //否则,放入队列
                queue.addLast(msg);
                //打印生产的消息
                System.out.println(Thread.currentThread().getName() + " 生产消息:" + msg.toString());
                queue.notifyAll();
            }
        }
    }
}
class Message {
    private int id;
    private String value;

    public Message(int id, String value) {
        this.id = id;
        this.value = value;
    }
    @Override
    public String toString() {
        return "Message{" +
                "id=" + id +
                ", value='" + value + '\'' +
                '}';
    }
}
public class ProducerAndConsumer1 {
    public static void main(String[] args) {
        MessageQueue<Message> queue = new MessageQueue<Message>(3);
        //创建2个生产者线程
        ExecutorService producer = Executors.newFixedThreadPool(2, new ThreadFactory() {
            private AtomicInteger id = new AtomicInteger(1);
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "生产者-" + id.getAndIncrement());
            }
        });
        //创建3个消费者线程
        ExecutorService consumer = Executors.newFixedThreadPool(3, new ThreadFactory() {
            private AtomicInteger id = new AtomicInteger(1);

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "消费者-" + id.getAndIncrement());
            }
        });
        for (int i = 0; i < 10; i++) {
            producer.submit(() -> {
                int id = new Random().nextInt(20);
                queue.put(new Message(id, "" + id));
            });
        }
        for (; ; ) {
            consumer.submit(() -> {
                queue.get();
            });
        }

    }
}

在这里插入图片描述

实现方式:

  1. 使用阻塞队列(LinkedBlockingQueue)实现消息队列存放消息。
  2. 使用Executors.newFixedThreadPool中的创建线程池的方法模拟生产者和消费者线程。

class Producer implements Runnable {

    private BlockingQueue queue;

    public Producer(BlockingQueue queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true) {
            int i = new Random().nextInt(50);
            String currName = Thread.currentThread().getName();

            //暂停毫秒
            try {
                TimeUnit.MILLISECONDS.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                queue.put(i);
                System.out.println("生产者线程-" + currName + " 生产了数据:" + i);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

class Consumer implements Runnable {

    private BlockingQueue queue;

    public Consumer(BlockingQueue queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true) {

            //暂停毫秒
            try {
                TimeUnit.MILLISECONDS.sleep(250);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String currName = Thread.currentThread().getName();
            try {
                int data = (int) queue.take();
                System.out.println("消费者线程-" + currName + " 消费了:" + data);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        }
    }
}

    public static void main(String[] args) {
        LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>(5);
        ExecutorService pool = Executors.newFixedThreadPool(5);
        Producer p1 = new Producer(queue);
        Producer p2 = new Producer(queue);
        Producer p3 = new Producer(queue);
        Consumer c1 = new Consumer(queue);
        Consumer c2 = new Consumer(queue);
        pool.execute(p1);
        pool.execute(p2);
        pool.execute(p3);
        pool.execute(c1);
        pool.execute(c2);
        pool.shutdown();
    }

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值