并发编程-生产者消费者模式Java代码实现

并发编程-生产者消费者模式Java代码实现

生产者消费者模式

  1. 生产者仅负责产生结果数据,不关心数据该如何处理,而消费者专心处理结果数据。

  2. 消息队列是有容量限制的,满时不会再加入数据,空时不会再消耗数据。

消费队列可以用来平衡生产和消费的线程资源。

并发编程-生产者消费者模式Java代码实现_第1张图片
代码如下:

  1. 使用双向链表和Synchronized锁来实现消息队列。
  2. 使用Excutors中的创建线程池的方法模拟生产者和消费者线程。

其他可见代码中的注释。

import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

public class MessageTest {
    public static void main(String[] args) {
        // 创建生产者线程池,总共3个线程,并且传入自定义的线程工厂,这样可以方便给线程起名
        ExecutorService producer = Executors.newFixedThreadPool(3, new ThreadFactory() {
            private AtomicInteger t = new AtomicInteger(1);
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "生产者-" + t.getAndIncrement());
            }
        });
        // 创建消费者线程池,总共3个线程,并且传入自定义的线程工厂,这样可以方便给线程起名
        ExecutorService consumer = Executors.newFixedThreadPool(2, new ThreadFactory() {
            private AtomicInteger t = new AtomicInteger(1);
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "消费者-" + t.getAndIncrement());
            }
        });

        // 新建消息队列,队列容量为2
        MessageQueue<Message> queue = new MessageQueue<>(2);

        // 生产者产生消息
        for (int i = 0; i < 5; i++) {
            int id = i;
            producer.submit(()->{
                queue.put(new Message(id, "value : " + id));
            });
        }

        // 消费者消费消息
        for (;;) {
            consumer.submit(()->{
                Message take = queue.take();
            });
        }
    }
}

class MessageQueue<T>{
    /*
      消息队列的定义,使用LinkedList作为存储消息的数据结构,这样可以将生产的消息直接放入
      队列尾部,消费消息时从头部直接获取。消息的类型定义为泛型。
     */
    //存储消息的链表
    private LinkedList<T> list = new LinkedList<>();
    // 消息队列容量
    private int capacity;

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

    public T take(){
        // 消费者线程取数据, 因为设计到多线程的对共享变量的访问,所以需要加锁,此处也可以使用ReentrantLock加锁
        // 此处对变量list加锁,也可以直接对this加锁
        synchronized (list){
            // 获得当前访问线程的名称
            String t_name = Thread.currentThread().getName();
            // 判断消息队列是否为空,为空则阻塞,生产者线程放入数据后唤醒
            while(list.isEmpty()){
                System.out.println("["+t_name+"]"+" 队列为空,消费者线程等待...");
                try {
                    list.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            // 不为空则取消息
            T message = list.removeFirst();
            // 打印操作信息
            System.out.println("["+t_name+"]"+" 消费消息: " + message.toString());
            // 唤醒阻塞中的线程
            list.notifyAll();
            return message;
        }
    }

    public void put(T message){
        // 生产者线程放入数据, 因为设计到多线程的对共享变量的访问,所以需要加锁,此处也可以使用ReentrantLock加锁
        // 此处对变量list加锁,也可以直接对this加锁
        synchronized (list){
            // 获得当前访问线程的名称
            String t_name = Thread.currentThread().getName();
            // 判断消息队列是否已满,满则阻塞,消费者线程消费数据后唤醒
            while(list.size()==capacity){
                System.out.println("["+t_name+"]"+" 队列已满,生产者线程等待...");
                try {
                    list.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            // 不满则放入消息
            list.addLast(message);
            // 打印操作信息
            System.out.println("["+t_name+"]"+" 生产消息: " + message.toString());
            // 唤醒阻塞中的线程
            list.notifyAll();
        }
    }
}

class Message{
    // 消息id
    private int id;
    // 消息的值
    private String value;

    public Message(int id, String value) {
        this.id = id;
        this.value = value;
    }

    public int getId() {
        return id;
    }

    public String getValue() {
        return value;
    }

    @Override
    public String toString() {
        return "Message{" +
                "id=" + id +
                ", value='" + value + '\'' +
                '}';
    }
}

运行结果:
并发编程-生产者消费者模式Java代码实现_第2张图片
可以看到首先消费者线程消费,但是队列为空,则线程阻塞等待,之后生产者-1和生产者-3生产消息,但是因为队列容量为2,当生产者-2想要继续放入消息时,被阻塞。之后消费者-2进行消费消息之后,生产者-2被唤醒又可以将消息放入到消息队列中去…

你可能感兴趣的:(JAVA基础,java,开发语言)