Java JUC并发编程

前言

1、JUC是指有关 java.util.concurrent包以及其子包,这些包都是有关线程操作的包
2、HTTPS服务请求中,WEB服务只负责创建主线程来接收外部的HTTPS请求,如果不做任何处理,默认业务逻辑是通过主线程来做的,如果业务执行时间较长且用户访问量较大的情况,WEB服务在单位时间内所能处理的用户请求就会有限,JUC并发编程的核心就是如何来释放主线成以及通过子线程来批量执行任务
3、JUC并发编程并不能提高执行任务所耗费的时间,但是可以极大的提高WEB容器的吞吐量

案例1(模拟下单)

Java JUC并发编程_第1张图片

  • 订单实体类
@Data
public class CreateOrderDto {
    /** 订单id */
    private String orderId;

    /** 库存id */
    private String stockId;

    /** 积分id */
    private String itegralId;

    /** 耗时,单位毫秒 */
    private String time;
}
  • Service代码
@Service
public class OrderTaskService {
    public CreateOrderDto create(CreateOrderDto createOrderDto) {
        createOrderDto.setOrderId("OrderTaskService:" + Thread.currentThread().getName());
        System.out.println("OrderTaskService:"+ JSONObject.toJSONString(createOrderDto));
        return createOrderDto;
    }
}

@Service
public class StockTaskService {
    public CreateOrderDto operate(CreateOrderDto createOrderDto) {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        createOrderDto.setStockId("StockFutureTaskService:" + Thread.currentThread().getName());
        System.out.println("StockFutureTaskService:"+JSONObject.toJSONString(createOrderDto));
        return createOrderDto;
    }
}

@Service
public class ItegralTaskService {
    public CreateOrderDto operate(CreateOrderDto createOrderDto) {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        createOrderDto.setItegralId("ItegralFutureTaskService:" + Thread.currentThread().getName());
        System.out.println("ItegralFutureTaskService:"+JSONObject.toJSONString(createOrderDto));
        return createOrderDto;
    }
}

同步处理

  • 创建订单
 @GetMapping("createOrderCommon")
 public CreateOrderDto createOrder() {
     long start = System.currentTimeMillis();
     // 本地服务 - 创建订单
     CreateOrderDto createOrderDto = new CreateOrderDto();
     createOrderDto = orderTaskService.create(createOrderDto);
     // 三方服务 - 处理库存
     createOrderDto = stockTaskService.operate(createOrderDto);
     // 三方服务 - 处理积分
     createOrderDto = itegralTaskService.operate(createOrderDto);
     long end = System.currentTimeMillis();

     createOrderDto.setTime("createOrderCommon任务耗时:" + String.valueOf(end - start));
     System.out.println("createOrderCommon主线程处理耗时:" + Thread.currentThread().getName() + " " + createOrderDto.getTime() + "毫秒");
     return createOrderDto;
 }
OrderTaskService{"orderId":"OrderTaskService:http-nio-8801-exec-1"}

StockFutureTaskService{
"orderId":"OrderTaskService:http-nio-8801-exec-1",
"stockId":"StockFutureTaskService:http-nio-8801-exec-1"
}

ItegralFutureTaskService{
"orderId":"OrderTaskService:http-nio-8801-exec-1",
"stockId":"StockFutureTaskService:http-nio-8801-exec-1",
"itegralId":"ItegralFutureTaskService:http-nio-8801-exec-1"
}

日志:createOrderCommon主线程处理耗时:http-nio-8801-exec-1 createOrderCommon任务耗时:1074毫秒
分析:从接收任务开始,容器的主线程一直被占用,直到业务处理结束才会释放主线程

Java JUC并发编程_第2张图片

异步处理(FutureTask)

  • 创建订单
@GetMapping("createOrderFutureTask")
public CreateOrderDto createOrderFutureTask() throws ExecutionException, InterruptedException {
    long start = System.currentTimeMillis();
    CreateOrderDto createOrderDto = new CreateOrderDto();

    // 本地服务 - 创建订单
    createOrderDto = orderTaskService.create(createOrderDto);

    // 三方服务 - 处理库存 TODO【异步】
    CreateOrderDto finalCreateOrderDto = createOrderDto;
    Callable<CreateOrderDto> callableForStock = new Callable<CreateOrderDto>() {
        @Override
        public CreateOrderDto call() throws Exception {
            return stockTaskService.operate(finalCreateOrderDto);
        }
    };

    // 三方服务 - 处理积分 TODO【异步】
    CreateOrderDto finalCreateOrderDto1 = createOrderDto;
    Callable<CreateOrderDto> callableForItegral = new Callable<CreateOrderDto>() {
        @Override
        public CreateOrderDto call() throws Exception {
            return itegralTaskService.operate(finalCreateOrderDto1);
        }
    };

    FutureTask<CreateOrderDto> futureForStock = new FutureTask<>(callableForStock);
    new Thread(futureForStock).start();

    FutureTask<CreateOrderDto> futureForItegral = new FutureTask<>(callableForItegral);
    new Thread(futureForItegral).start();

    createOrderDto.setStockId(futureForStock.get().getStockId()); // TODO 阻塞式获取执行结果,所以会占用web容器的主线程
    createOrderDto.setItegralId(futureForStock.get().getItegralId()); // TODO 阻塞式获取执行结果,所以会占用web容器的主线程

    long end = System.currentTimeMillis();
    createOrderDto.setTime("createOrderFutureTask任务耗时:" + String.valueOf(end - start));
    System.out.println("createOrderFutureTask主线程处理耗时:" + Thread.currentThread().getName() + " " + createOrderDto.getTime() + "毫秒");
    return createOrderDto;
}
OrderTaskService{"orderId":"OrderTaskService:http-nio-8801-exec-2"}

StockFutureTaskService{
"orderId":"OrderTaskService:http-nio-8801-exec-2",
"stockId":"StockFutureTaskService:Thread-15",
"itegralId":"ItegralFutureTaskService:Thread-16"
}

ItegralFutureTaskService{
"orderId":"OrderTaskService:http-nio-8801-exec-2",
"stockId":"StockFutureTaskService:Thread-15",
"itegralId":"ItegralFutureTaskService:Thread-16"
}

日志:createOrderFutureTask主线程处理耗时:http-nio-8801-exec-2 createOrderFutureTask任务耗时:517毫秒
分析:通过FutureTask实现多线程并发处理库存和积分业务,任然会阻塞主线程,但相比于同步处理,可大大减少主线程被阻塞的时间

使用Callable对FutureTask优化

Asynchronous Requests

DeferredResult和Callable都是为了异步生成返回值提供基本支持。一个请求进来,在没有得到返回数据之前,DispatcherServlet和所有Filter就会退出Servlet容器线程,但响应保持打开状态,一旦返回数据有了,DispatcherServlet就会被再次调用并且处理,以异步产生的方式向请求端响应值。这么做的好处就是请求不会长时间占用服务连接池,提高服务器的吞吐量

  • 创建订单
@GetMapping("createOrderCallable")
public Callable<CreateOrderDto> createOrderCallable() throws ExecutionException, InterruptedException {
    long start = System.currentTimeMillis();

    Callable<CreateOrderDto> callable = new Callable<CreateOrderDto>() {
        @Override
        public CreateOrderDto call() throws Exception {
            return createOrderFutureTask();
        }
    };

    long end = System.currentTimeMillis();
    System.out.println("createOrderCallable主线程处理耗时:" + Thread.currentThread().getName() + " " +(end - start) + "毫秒");

    return callable;
}

OrderTaskService{"orderId":"OrderTaskService:MvcAsync2"}

StockFutureTaskService{
"orderId":"OrderTaskService:MvcAsync2",
"stockId":"StockFutureTaskService:Thread-19",
"itegralId":"ItegralFutureTaskService:Thread-20"
}

ItegralFutureTaskService{
"orderId":"OrderTaskService:MvcAsync2",
"stockId":"StockFutureTaskService:Thread-19",
"itegralId":"ItegralFutureTaskService:Thread-20"
}

日志:
createOrderCallable主线程处理耗时:http-nio-8801-exec-7 0毫秒
createOrderFutureTask主线程处理耗时:MvcAsync2 createOrderFutureTask任务耗时:501毫秒

分析:通过Callable优化后,使得FutureTask阻塞的线程对象由原来的主线程改为MvcAsync程

异步处理(CompletableFuture)

/**
 * @描述: TODO 自定义线程池来处理异步任务。在方法上添加:@Async("api-asyn-taskExecutor")
 * @作者: lixing [email protected]
 * @日期 2020/2/19 21:18
 */
@Component
@Configuration
public class ThreadPoolTaskExecutorConfig {
    @Bean("api-asyn-taskExecutor")
    public Executor taskExecutorPool() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setThreadNamePrefix("自定义异步线程池"); // 线程名字前缀
        threadPoolTaskExecutor.setCorePoolSize(5); // 核心线程数
        threadPoolTaskExecutor.setQueueCapacity(10); // 任务队列容量(缓存队列)
        threadPoolTaskExecutor.setMaxPoolSize(10); //线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        threadPoolTaskExecutor.setKeepAliveSeconds(120); // 允许空闲时间,当超过了核心线程数之外的线程在空闲时间到达之后会被销毁
        threadPoolTaskExecutor.setRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy() ); // 任务拒绝策略
        // 关闭线程池时是否等待当前调度任务完成后才开始关闭
        threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown( true ); // 等待
        threadPoolTaskExecutor.setAwaitTerminationSeconds( 60 ); // 等待时长
        threadPoolTaskExecutor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                // 异常统一处理......发送邮件、发送短信
            }
        });
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }
}
  • 创建订单
/** 注入自定义线程池 */
@Resource
private ThreadPoolTaskExecutorConfig threadPoolTaskExecutorConfig;

@GetMapping("createOrderCompletableFuture")
public CreateOrderDto createOrderCompletableFuture() throws ExecutionException, InterruptedException {
    long start = System.currentTimeMillis();
    
    CreateOrderDto createOrderDto = new CreateOrderDto();

    /** 
     * 异步创建订单
     */
    CompletableFuture<CreateOrderDto> supplyAsyncForOrder = CompletableFuture.supplyAsync(() -> {
        return orderTaskService.create(createOrderDto);
    }, threadPoolTaskExecutorConfig.taskExecutorPool());
    
    /**
     * 订单创建成功后,异步处理库存和积分
     */
    CompletableFuture<CreateOrderDto> thenApplyForOrder = supplyAsyncForOrder.thenApply(createOrderDtoTemp -> {
        CompletableFuture<CreateOrderDto> itegral = CompletableFuture.supplyAsync(() -> {
            return itegralTaskService.operate(createOrderDtoTemp);
        }, threadPoolTaskExecutorConfig.taskExecutorPool());

        CompletableFuture<CreateOrderDto> stock = CompletableFuture.supplyAsync(() -> {
            return stockTaskService.operate(createOrderDtoTemp);
        }, threadPoolTaskExecutorConfig.taskExecutorPool());

        return createOrderDtoTemp;
    });

    long end = System.currentTimeMillis();
    System.out.println("createOrderCompletableFuture主线程处理耗时:" + Thread.currentThread().getName() + " " +(end - start) + "毫秒");

    return thenApplyForOrder.get();
}

OrderTaskService{"orderId":"OrderTaskService:自定义异步线程池4"}

StockFutureTaskService{
"orderId":"OrderTaskService:自定义异步线程池4",
"stockId":"StockFutureTaskService:自定义异步线程池4",
"itegralId":"ItegralFutureTaskService:自定义异步线程池5"
}

ItegralFutureTaskService{
"orderId":"OrderTaskService:自定义异步线程池4",
"stockId":"StockFutureTaskService:自定义异步线程池4",
"itegralId":"ItegralFutureTaskService:自定义异步线程池5"
}

日志:createOrderCompletableFuture主线程处理耗时:http-nio-8801-exec-2 0毫秒
分析:使用CompletableFuture可以使主线程接收请求后立即释放,将具体的业务交由具体的子线程来处理,最终再由子线程进行响应

Java JUC并发编程_第3张图片

案例2(模拟批处理下单)

1、需求:订单创建成功后,根据订单号处理积分(模拟耗时0.5秒)、根据订单号处理库存(模拟耗时0.5秒)
2、压测:9999的qps,持续10秒

业务代码

 @GetMapping("batchOrder")
 public CreateOrderDto batchOrder() throws ExecutionException, InterruptedException {
     long start = System.currentTimeMillis();

     CompletableFuture<CreateOrderDto> supplyAsyncForOrder = CompletableFuture.supplyAsync(() -> {
         CreateOrderDto createOrderDto = null;
         try {
             createOrderDto = orderBashService.batchOrder();
         } catch (ExecutionException e) {
             throw new RuntimeException(e);
         } catch (InterruptedException e) {
             throw new RuntimeException(e);
         }
         return createOrderDto;
     });

     long end = System.currentTimeMillis();
     System.out.println(Thread.currentThread().getName() + "batchOrder主线程被占用时间:" + (end - start));
     return supplyAsyncForOrder.get();
 }
package com.snoob.springboot.bffDemo1;

import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.*;

@Service
public class OrderBashService {
    @Resource
    private OrderTaskService orderTaskService;

    @Resource
    private ItegralTaskService itegralTaskService;

    @Resource
    private StockTaskService stockTaskService;

    @Resource
    private ThreadPoolTaskExecutorConfig threadPoolTaskExecutorConfig;

    // 链表阻塞式队列,适合添加和删除操作
    // 生产环境,请使用中间件队列(Redis),即时服务挂了,也不会都是订单
    private static final LinkedBlockingDeque<OrderBatch> linkedBlockingDeque = new LinkedBlockingDeque<>();

    // 该定时任务随项目启动
    @PostConstruct
    public void doBusiness() {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                int linkedBlockingDequeSize = linkedBlockingDeque.size();
                //System.out.println("当前时间:" + JSONObject.toJSONString(new Date()) + " 待批处理数据:" + linkedBlockingDequeSize);

                if (linkedBlockingDequeSize == 0) {
                    return;
                }

                // 重新封装待处理的订单
                List<OrderBatch> orderBatchList = new ArrayList<>();
                for (int i = 0; i < linkedBlockingDequeSize; i++) {
                    OrderBatch orderBatch = linkedBlockingDeque.poll();
                    orderBatchList.add(orderBatch);
                }

                // 线程池-异步并发处理 orderBatchList 订单
                orderBatchList.forEach(orderBatchTemp -> {
                    CreateOrderDto createOrderDtoTemp = orderBatchTemp.getCreateOrderDto();

                    // 订单处理
                    orderTaskService.create(createOrderDtoTemp);

                    // 异步处理库存
                    threadPoolTaskExecutorConfig.taskExecutorPool().execute(new Runnable() {
                        @Override
                        public void run() {
                            stockTaskService.operate(createOrderDtoTemp);
                        }
                    });

                    // 异步处理积分
                    threadPoolTaskExecutorConfig.taskExecutorPool().execute(new Runnable() {
                        @Override
                        public void run() {
                            itegralTaskService.operate(createOrderDtoTemp);
                        }
                    });

                    orderBatchTemp.getCompletableFuture().complete(createOrderDtoTemp);
                });
            }
        }, 10000, 20, TimeUnit.MILLISECONDS);
    }

    /**
     * 订单批处理
     */
    public CreateOrderDto batchOrder() throws ExecutionException, InterruptedException {
        CreateOrderDto createOrderDto = new CreateOrderDto();
        createOrderDto.setUuid(UUID.randomUUID().toString());
        CompletableFuture<CreateOrderDto> completableFuture = new CompletableFuture<>();
        //
        OrderBatch orderBatch = new OrderBatch();
        orderBatch.setCreateOrderDto(createOrderDto);
        orderBatch.setCompletableFuture(completableFuture);
        linkedBlockingDeque.add(orderBatch);
        //
        return completableFuture.get();
    }
}

Java JUC并发编程_第4张图片

你可能感兴趣的:(Java,java,jvm)