Java 并发中的设计模式

发布时间 2023-03-26 11:36:32作者: Dazzling!

其实在 Java 并发编程这个领域中,隐藏了许多的“设计模式”,并发编程的设计模式和我们常谈的“单例模式”、“工厂模式”这类“设计模式” ,其实可以理解为都是对代码精良设计的思想提炼。

Producer Consumer 模式

Producer-Consumer 模式是大众们使用最多的模式之一,它的主要表现形式可以用下边一张图来解释:

image.png

生产者往一个缓冲区中投递元素,接着消费者从这个缓冲区中去提取元素,这方面的具体代表如 JDK 中的 java.util.concurrent.BlockingQueue 类,这个类虽然实现了 java.util.Queue 接口,也都提供了 offerpoll 方法,但是要想利用它的阻塞效果,还是得使用它的 puttake 方法。

另外在它的底层实现方面可以选择的种类有很多种,诸如 ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue 、DelayQueue、ConcurrentLinkedQueue。

下边我给出一段比较经典的 Producer-Consumer 模式代码,带大家理解下这种模式:

//消息生产者
public class Producer {

    private ArrayBlockingQueue<String> queue;

    public Producer(ArrayBlockingQueue<String> queue) {
        this.queue = queue;
    }

    public boolean put(String content){
        try {
            queue.put(content);
            return true;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

    public void take(){
        queue.poll();
    }
}

//消息消费者
public class Consumer {

    private ArrayBlockingQueue<String> queue;

    public Consumer(ArrayBlockingQueue<String> queue) {
        this.queue = queue;
    }

    public void start() {
        Thread t = new Thread(new Runnable() {
            public void run() {
                while (true) {
                    try {
                        String content = queue.take();
                        System.out.println("消费数据:" + content);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t.start();
    }
}

//测试代码
public class TestDemo {
    public static ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(20);

    public static void main(String[] args) throws InterruptedException {
        Producer producer = new Producer(queue);
        Consumer consumer = new Consumer(queue);
        consumer.start();
        while (true){
            System.out.println("投递元素");
            producer.put(UUID.randomUUID().toString());
            Thread.sleep(2000);
        }
    }

}

Producer 类内部会往一个阻塞队列中投递消息,而 Consumer 类中会有一个线程不断地从阻塞队列中取出消息,一投一取,从而形成了生产者/消费者的模式。

那么,什么场景下会使用这种模式呢?

在面对一些突发流量的时候,可以尝试利用生产者/消费者的模式来进行削减。 例如 Nacos 底层对于心跳包的处理机制就是采用了这种方式,在内存中设计了一条阻塞队列,用于接收各个注册方发送过来的心跳包数据,然后在 Nacos 的底层会有一条线程专门处理这些心跳包数据,整体流程如下图所示:

image.png

这种设计可以有效应对“当有上千节点同时往nacos中心发送心跳包”所带来的高并发请求问题。