spring+ThreadPoolTaskExecutor+@Async+Future实现异步调用

前提

最近做了一个手机网站,进入首页会有六个模块的内容需要访问后台。去获取对应的数据。这样是很占带宽和占用cpu,频繁的查询也很消耗数据库的性能。

解决办法

解决办法就是把多条请求合并成一条,这样就节约了带宽浪费的问题。因为java是线程同步的,所以多个请求一起的话,会增加查询数据库的时间。解决办法就是使用多线程,多个线程同时去查询数据库,减少等待时间。因为首页数据是经常访问的。所以会频繁的访问数据库。这个问题的解决办法是使用缓存数据库,redis。先查询redis,看是不是有数据,有的话就返回,没有的话就查询数据库,再把结果存入redis,就可以加快首页访问的响应速度。spring整合redis+redisTemplate教程的链接:添加链接描述

1.新建多线程配置类ThreadConfig

代码如下:

package com.bbkj.common.thread;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

/**
 * @author JJ
 * @version 1.0
 * @description: TODO
 * @date 2022/1/4 20:20
 */
@Configuration
@ComponentScan("com.bbkj.common.thread")
@EnableAsync //启用一步任务
public class ThreadConfig {
    // 这里声明一个bean,类似于xml中的<bean>标签。
    // Executor就是一个线程池
    @Bean("asyncTaskExecutor")
    public Executor getExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(7);
        executor.setMaxPoolSize(7);
        executor.setQueueCapacity(18);
        executor.initialize();
        return executor;
    }
}

这里我是按照并发任务的数量去设置核心线程数和最大线程数的,使用的时候可以按照自己项目的情况去设定。

2.使用@Async去执行异步方法,注入spring容器。

package com.bbkj.service.impl;

import com.bbkj.common.utils.Page;
import com.bbkj.domain.Carousel;
import com.bbkj.domain.Catalog;
import com.bbkj.service.CarouselService;
import com.bbkj.service.CatalogService;
import com.bbkj.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.criterion.DetachedCriteria;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;

/**
 * @author JJ
 * @version 1.0
 * @description: TODO 新建异步方法
 * @date 2022/1/5 9:28
 */
@Slf4j
@Service
public class AsynTaskService {
    @Autowired
    private CarouselService carouselService;

    @Autowired
    private ProductService productService;

    @Autowired
    private CatalogService catalogService;

    /*
    这里可以注入spring中管理的其他bean,这也是使用spring来实现多线程的一大优势
    @Async 标注此任务为异步任务,在执行此方法的时候,会单独开启线程来执行
     */
    @Async
    public Future<Page> getCarousel() {
        DetachedCriteria criteria = DetachedCriteria.forClass(Carousel.class);
        Page page = carouselService.pageList(criteria, 1, 8);
        log.info("轮播图获取完成");
        return new AsyncResult<>(page);
    }

    @Async
    public Future<Page> getRecommend() throws Exception {
        //page为取出的那页数据
        try {
            Page page = productService.findByRecommend(1, 1, 5);
            log.info("推荐商品获取完成");
            return new AsyncResult<>(page);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @return java.util.concurrent.Future<com.bbkj.common.utils.Page>
     * @author JJ
     * @Description 有返回值
     * @Date 2022/1/6 15:57
     **/
    @Async
    public Future<Page> getCatalog() throws Exception {
        try {
            List result = new ArrayList();
            //带分页器
            Page page = catalogService.findFather(1, 5);
            List<Catalog> catalogs = page.getPageList();
            setSort(catalogs, result);
            log.info("商品分类获取完成");
            return new AsyncResult<>(page);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //包装分类,sorts1是二级分类,sort是操作的一级分类
    public List setSort(List<Catalog> catalogs, List result) {
        for (Catalog item : catalogs) {
            List<Catalog> sorts1 = catalogService.findChild(item.getId());
            Map map = new HashMap();
            map.put("catalogs", sorts1);
            map.put("icon", item.getIcon());
            map.put("catalog_name", item.getCatalog_name());
            map.put("paren_id", item.getParen_id());
            map.put("id", item.getId());
            result.add(map);
        }
        return result;
    }
}

注意:这里的@Async注解作用的方法。需要返回值的话,只能是用Future<希望返回的类型>来返回,直接返回其他类型没用。

还可以使用其他方式提交异步任务,如下

@Autowired
    AsyncTaskExecutor asyncTaskExecutor;//注入线程池对象

    //通过线程池对象提交异步任务
    asyncTaskExecutor.submit(() -> {
        log.info("异步任务开始");
        
        //省略异步任务业务逻辑...

        log.info("异步任务结束");
    });

3.调用异步方法

package com.bbkj.action;

import com.alibaba.fastjson.JSON;
import com.bbkj.common.BaseAction;
import com.bbkj.common.redis.RedisUtil;
import com.bbkj.service.impl.AsynTaskService;
import com.bbkj.common.utils.Page;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;

/**
 * @author JJ
 * @version 1.0
 * @description: TODO
 * @date 2022/1/4 20:19
 */
@Getter
@Setter
@Controller
@ParentPackage("post")
@Namespace("/base")
@Slf4j
public class BaseActionSync extends BaseAction {
    @Autowired
    private AsynTaskService asynTaskService;

    @Autowired
    private RedisUtil redisUtil;

    @Action("index-data")
    public String indexData() {
        Map map = new HashMap();
        try {
            String indexData = redisUtil.get("indexData");
            if (indexData != null && indexData.length() > 0 && !indexData.isEmpty()) {
                // redis缓存里面有值,使用缓存的值
                log.info("redis缓存里有值: " + indexData);
                Map result = JSON.parseObject(indexData, Map.class);
                returnData(200, "true", result);
                return null;
            }
            log.info("redis没有缓存数据,把数据找出来,再缓存到redis,方便下次快速获取!");
            Future<Page> carouselPage = asynTaskService.getCarousel();
            Future<Page> recommendPage = asynTaskService.getRecommend();
            Future<Page> catalogPage = asynTaskService.getCatalog();
            map.put("carousel", carouselPage.get().getPageList());
            map.put("recommend", recommendPage.get().getPageList());
            map.put("catalog", catalogPage.get().getPageList());
            JSONObject result = JSONObject.fromObject(map);
            redisUtil.set("indexData", result.toString(), 1, "d");
            returnData(200, "true", result);
            //log.info("全部数据获取完成");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值