最近在 B站 看到有关于利用 多线程 进行 业务任务的内存合并处理 的例子觉得很好,在此做一下记录。
主要内容是:将多个用户请求合并成一个请求队列,利用 多线程 进行并行处理,达到批量处理请求、提高服务 dps 的目的。
关键点:线程间通信。
Demo 视频链接:https://www.bilibili.com/video/BV1Hv4y1P7ta/?spm_id_from=333.788&vd_source=8b6dfb9416c7aad22744de7bcdae9a95
Executor 框架 是 Java 5 引进的用于 启动、调度和管理线程的 API,其内部使用了线程池机制,同时也是一个 基于 生产者-消费者模式 的异步执行框架,其提交任务的线程相当于生产者、执行任务的线程相当于消费者(使用 Runnable 表示任务),处于 java.util.cocurrent 包下。
线程池
Executor
顶层接口。
Executors
工厂类,用于创建线程池,其返回的线程池都实现了 ExecutorService 接口。
ExecutorService
Executor 的子接口,其提供了生命周期管理的方法,返回 Future 对象 和 可跟踪一个或多个异步任务执行状况的方法。
CompletionService
Future
Callable
扩展:
CountDownLatch 类:允许一个或者多个线程去等待其他线程完成操作,接收一个 int 类型的参数,表示要等待的工作线程的个数。
应用场景:
方法 | 说明 |
---|---|
CountDownLatch(int count) | 构造方法,创建一个值为 count 的计数器(latch) |
await() | 使得当前线程进入同步队列进行等待,直到 latch 的值被减到 0 或者当前线程被中断,当前线程就会被唤醒 |
await(long timeout, TimeUnit unit) | 带超时时间的 await() |
countDown() | 使 latch 的值减 1,如果减到了 0,则会唤醒所有等待在这个 latch 上的线程 |
getCount() | 获得 latch 的值 |
package com.hong.demo.thread_merger;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class KillDemo {
/**
* 启动 10 个线程
* 库存 6 个
* 生成一个合并队列
* 每个用户能拿到自己的请求响应
*/
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
KillDemo killDemo = new KillDemo();
killDemo.mergeJob();
Thread.sleep(2000);
List<Future<Result>> futureList = new ArrayList<>();
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 6; i++) {
final Long orderId = i + 100L;
final Long userId = Long.valueOf(i);
Future<Result> future = executorService.submit(() -> {
countDownLatch.countDown();
countDownLatch.await(1000, TimeUnit.MILLISECONDS);
return killDemo.operate(new UserRequest(orderId, userId, 1));
});
futureList.add(future);
}
futureList.forEach(future -> {
try {
Result result = future.get(300, TimeUnit.MILLISECONDS);
System.out.println(Thread.currentThread().getName() + ":客户端请求响应:" + result);
} catch (Exception e) {
e.printStackTrace();
}
});
}
// 模拟数据库行
private Integer stack = 10;
private BlockingQueue<RequestPromise> queue = new LinkedBlockingQueue<>(10);
/**
* 用户库存扣减
*
* @param userRequest
*/
public Result operate(UserRequest userRequest) throws InterruptedException {
// TODO 阈值判断
// TODO 队列的创建
RequestPromise requestPromise = new RequestPromise(userRequest);
synchronized (requestPromise) {
boolean enqueueSuccess = queue.offer(requestPromise, 100, TimeUnit.MILLISECONDS);
if (!enqueueSuccess) {
return new Result(false, "系统繁忙");
}
try {
requestPromise.wait(200);
if (requestPromise.getResult() == null) {
return new Result(false, "等待超时");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return requestPromise.getResult();
}
/**
* 开启一个异步线程,执行任务
*/
public void mergeJob() {
new Thread(() -> {
List<RequestPromise> list = new ArrayList<>();
while (true) {
if (queue.isEmpty()) {
try {
Thread.sleep(10);
continue;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int batchSize = queue.size();
for (int i = 0; i < batchSize; i++) {
list.add(queue.poll());
}
System.out.println(Thread.currentThread().getName() + ":合并扣减库存" + list);
int sum = list.stream().mapToInt(e -> e.getUserRequest().getCount()).sum();
// 两种情况
// 1. 总量未超库存
if (sum <= stack) {
stack -= sum;
// notify user
list.forEach(requestPromise -> {
requestPromise.setResult(new Result(true, "ok"));
synchronized (requestPromise) {
requestPromise.notify();
}
});
continue;
}
// 2. 总量超库存,退化成串行处理
for (RequestPromise requestPromise : list) {
int count = requestPromise.getUserRequest().getCount();
if (count <= stack) {
stack -= count;
requestPromise.setResult(new Result(true, "ok"));
} else {
requestPromise.setResult(new Result(false, "库存不足"));
}
synchronized (requestPromise) {
requestPromise.notify();
}
}
list.clear();
}
}, "mergeThread").start();
}
}
// 锁资源
class RequestPromise {
private UserRequest userRequest;
private Result result;
public RequestPromise(UserRequest userRequest) {
this.userRequest = userRequest;
}
public RequestPromise(UserRequest userRequest, Result result) {
this.userRequest = userRequest;
this.result = result;
}
public UserRequest getUserRequest() {
return userRequest;
}
public void setUserRequest(UserRequest userRequest) {
this.userRequest = userRequest;
}
public Result getResult() {
return result;
}
public void setResult(Result result) {
this.result = result;
}
@Override
public String toString() {
return "RequestPromise{" +
"userRequest=" + userRequest +
", result=" + result +
'}';
}
}
// 结果通知
class Result {
private Boolean success;
private String msg;
public Result(Boolean success, String msg) {
this.success = success;
this.msg = msg;
}
public Boolean getSuccess() {
return success;
}
public void setSuccess(Boolean success) {
this.success = success;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
@Override
public String toString() {
return "Result{" +
"success=" + success +
", msg='" + msg + '\'' +
'}';
}
}
// 用户请求
class UserRequest {
private Long orderId;
private Long userId;
private Integer count;
public UserRequest(Long orderId, Long userId, Integer count) {
this.orderId = orderId;
this.userId = userId;
this.count = count;
}
public Long getOrderId() {
return orderId;
}
public void setOrderId(Long orderId) {
this.orderId = orderId;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
@Override
public String toString() {
return "UserRequest{" +
"orderId=" + orderId +
", userId=" + userId +
", count=" + count +
'}';
}
}