Java 并发中的设计模式
其实在 Java 并发编程这个领域中,隐藏了许多的“设计模式”,并发编程的设计模式和我们常谈的“单例模式”、“工厂模式”这类“设计模式” ,其实可以理解为都是对代码精良设计的思想提炼。
Producer Consumer 模式
Producer-Consumer 模式是大众们使用最多的模式之一,它的主要表现形式可以用下边一张图来解释:
生产者往一个缓冲区中投递元素,接着消费者从这个缓冲区中去提取元素,这方面的具体代表如 JDK 中的 java.util.concurrent.BlockingQueue 类,这个类虽然实现了 java.util.Queue 接口,也都提供了 offer 和 poll 方法,但是要想利用它的阻塞效果,还是得使用它的 put 和 take 方法。
另外在它的底层实现方面可以选择的种类有很多种,诸如 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 的底层会有一条线程专门处理这些心跳包数据,整体流程如下图所示:
这种设计可以有效应对“当有上千节点同时往nacos中心发送心跳包”所带来的高并发请求问题。