Java 多線程的正確使用姿勢

昨天線上的導出文件數據出現了問題(導出的數據= A表通過sql查出來+B表通過異步接口查詢出來數據拼裝),經過排查是由於我使用了Spring core包中的ThreadPoolTaskExecutor類去異步獲取另一個B表中的數據,B表中的數據還沒有完全返回回來。接口就已經返回了,所以導致導出數據問題。

1. 導出數據會丟失的代碼如下:

<!-- 通用異步執行器 -->
	<bean id="taskExecutor"
		  class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
		<property name="corePoolSize" value="10" />
		<property name="maxPoolSize" value="30" />
	</bean>
 @Autowired
    TaskExecutor taskExecutor;

List<OrderDto> orderDtoList = new ArrayList<>();
        List<Order> orderList = OrderService.selectOrderList(query);
        for (Order brokerOrder : orderList) {

            OrderDto orderDto = new OrderDto();
            BeanUtils.copyProperties(brokerOrder, orderDto);
            taskExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 查計劃信息
                        AmountQuery amountQuery = new AmountQuery();
                        amountQuery.setOrderId(brokerOrder.getOrderId());
                        amountQuery.setOrderCode(brokerOrder.getOrderCode());
                        Return<Result<AmountDto>> amountList = amountFacadeService.selectOrderPayAmountList(amountQuery);
                        Result pageResult = amountList.R;
                        if (pageResult != null && pageResult.getData() != null && pageResult.getData().size() > 0) {
                            List<AmountDto> alist = new ArrayList<>();
                            List<AmountDto> list = pageResult.getData();
                            for (AmountDto dto : list) {
                                alist.add(dto);
                            }
                            orderDto.setAmountList(alist);
                        }
                    } catch (Exception e) {
                        LOG.info("error ",e);
                    }
                }
            });

            orderDtoList.add(orderDto);
        }

        int count = OrderService.selectOrderListCount(query);
        return Return.create(new Result<OrderDto>(count, orderDtoList));

2. 然後換成了多線程的Futrue後,問題解決,部分代碼如下:

private final ExecutorService es = new ThreadPoolExecutor(8, 10, 100, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(5000));        

List<TruckBrokerOrderDto> orderDtoList = new ArrayList<>();
        List<order> orderList = orderService.selectOrderList(query);

        Map<String, Future<orderDto>> futureMap = new HashMap<>();
        for (Order order : orderList) {

            Future<OrderDto> orderFuture = es.submit(new Callable<OrderDto>() {
                @Override
                public OrderDto call() {

                        OrderDto orderAmountDto = new OrderDto();

                        // 查計劃信息
                        AmountQuery amountQuery = new AmountQuery();
                        amountQuery.setOrderId(order.getOrderId());
                        amountQuery.setOrderCode(order.getOrderCode());
                        List<Amount> amountList = amountService.selectAmountList(amountQuery);
                        if (amountList != null && amountList.size() > 0) {

                            //  數據處理
                        }

                        return orderAmountDto;
                    }
            });

            futureMap.put(order.getOrderCode(), orderFuture);
        }

        for (Order order  : orderList) {
            OrderDto orderDto = new OrderDto();
            BeanUtils.copyProperties(brokerOrder, orderDto);

            try {
                OrderDto orderAmountDto = futureMap.get(order.getOrderCode()).get();
                if (orderAmountDto.getAmountList() != null && orderAmountDto.getAmountList().size() > 0) {
                    orderDto.setAmountList(orderAmountDto.getAmountList());
                }
            } catch (Exception e) {
                LOG.info("error " ,e);
            }

            orderDtoList.add(orderDto);
        }

        int count = OrderService.selectOrderListCount(query);
        returnReturn.create(new Result<OrderDto>(count, orderDtoList));

總結:

一般不需要對異步返回數據做處理的話,可以直接用Spring core包中的ThreadPoolTaskExecutor類直接執行(會丟失數據),比如發送短信,異步更新數據不需要返回結果等。

假如要對異步返回數據做處理的話,請用多線程的Futrue模式,可以保證數據不丟失。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章