【JAVA】向 Callable / Runnable 内部传递参数,在 SpringBoot 中使用


1. oracle articles 中的例子

oracle 文章:分解和合并:Java 也擅长轻松的并行编程!
简单例子 by Julien Ponge:

import java.util.*;
import java.util.concurrent.*;
import static java.util.Arrays.asList;

public class Sums {
    // Callable,有返回值
    static class Sum implements Callable<Long> {
    	// 传入参数: from 和 to
        private final long from;
        private final long to;
        // 从带参的构造器这里获得传入的参数:from 和 to
        Sum(long from, long to) {
            this.from = from;
            this.to = to;
        }
        // 返回Long类型的自加结果
        @Override
        public Long call() {
            long acc = 0;
            // 从0开始的自加循环,从from计数到to
            for (long i = from; i <= to; i++) {
                acc = acc + i;
            }
            return acc;
        }      
    }
    
    public static void main(String[] args) throws Exception {
        
        ExecutorService executor = Executors.newFixedThreadPool(2);
        List <Future<Long>> results = executor.invokeAll(asList(
            new Sum(0, 10), new Sum(100, 1_000), new Sum(10_000, 1_000_000)
        ));
        executor.shutdown();
        
        for (Future<Long> result : results) {
            System.out.println(result.get());
        }      
    }  
}

2. 在 SpringBoot 中调用

作为新手使用,仿写简单类:

/**
 * 文件下载工具类
 * @author jok
 * @date 2019/8/28 15:25
 */
public class DownloadUtils {

	/**
    * 下载文件
    * Callable:可自定义返回值
    * @author jok
    * @date 2019/8/28 15:25
    */
    public class DownloadFileCall implements Callable<String>
    {
        private final Integer fileId;
        private final FileInfoService fileInfoService;
        // 带参构造器为public,否则无法被外部访问到
        // 从带参的构造器这里获得传入的参数:ID、Service 等
        public DownloadFileCall(Integer fileId , FileInfoService fileInfoService) {
            this.fileId = fileId ;
            this.fileInfoService = fileInfoService;
        }
		// 可自定义返回值
        @Override
        public String call() {
			// TO-DO your code
			// pojo类FileInfo:放点简单的整数文件ID和文件名字符串
			FileInfo fileInfo = this.fileInfoService.getInfoById(fileId);
			System.out.println("fileId = " + fileId);
            return fileInfo.toString();
        }

    }

    /**
     * 下载文件
     * Runnable,无返回值
     * @author jok
     * @date 2019/9/17 23:30
     */
    public class DownloadFileRun implements Runnable {

        private final Integer fileId;
        private final FileInfoService fileInfoService;
        // 带参构造器为public,否则无法被外部访问到
        // 从带参的构造器这里获得传入的参数:ID、Service 等
        public DownloadFileRun(Integer fileId, FileInfoService fileInfoService) {
            this.fileId = fileId;
            this.fileInfoService = fileInfoService;
        }
        // 无返回值
        @Override
        public void run() {
			// TO-DO your code
			FileInfo fileInfo = this.fileInfoService.getInfoById(fileId);
			System.out.println("fileId = " + fileId);
            System.out.println(fileInfo.toString());
        }
        
    }
    
}    

于是,在其他类中实例化带参数的 Callalbe / Runnable,并且运行里面的 call() / run():

		// 实例化
        DownloadUtils downloadUtils = new DownloadUtils();
        // 传入参数 ID、Service
        // 如果带参的构造器是private,这里就用不了
        DownloadUtils.DownloadFileRun downloadFileRun
                = downloadUtils.new DownloadFileRun(fileId, fileInfoService);
        DownloadUtils.DownloadFileCall downloadFileCall
                = downloadUtils.new DownloadFileCall(fileId, fileInfoService);
		// 接收Callable的返回值
		String fileInfo = downloadFileCall.call(1, this.fileInfoService);
		
		downloadFileRun.run(1, this.fileInfoService);

在SpringBoot中,上面的Runnable作为异步(定时)任务调用:
参考了:SpringBoot中并发定时任务的实现、动态定时任务的实现

/**
 *ClassName: DynamicTimedTask
 *Description: 定时任务、动态任务
 *@author zhuanlan.zhihu.com/p/61526583
 *@date 2019/4/5
 *@version 1.0 
 **/
@Component(value = "dynamicTimedTask")
@Log4j2
public class DynamicTimedTask {

	@Autowire
	private final FileInfoService fileInfoService;
	public DynamicTimedTask(FileInfoService fileInfoService) {
		fileInfoService = this.fileInfoService;
	} 
	
    /**
     * 接受任务的返回结果
     */
    private ScheduledFuture<?> future;

    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;
    
    /**
     * 实例化一个线程池任务调度类,可以使用自定义的 ThreadPoolTaskScheduler
     * @return org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
     */
    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        // 定义线程池数量
        threadPoolTaskScheduler.setPoolSize(5);
		// 给线程自定义一个名称
        threadPoolTaskScheduler.setThreadNamePrefix("threadPoolTaskScheduler-");
        // 调度器shutdown被调用时等待当前被调度的任务完成,默认为false
        threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(false);
        // 等待时长
        threadPoolTaskScheduler.setAwaitTerminationSeconds(60);

        return threadPoolTaskScheduler;
    }
    
    /**
     * 开启定时任务:下载文件
     * 间隔60秒
     * @param fileId 文件信息ID
     * @param fileInfoService 文件信息服务
     * @author jok
     * @date 2019/9/17 23:27
    */
    @Async
    public void startDownloadFile(Integer fileId, FileInfoService fileInfoService)
    {
    	// 如果带参的构造器是private,这里就用不了
        DownloadUtils downloadUtils = new DownloadUtils();
        DownloadUtils.DownloadFileRun downloadFileRun
                = downloadUtils.new DownloadFileRun(fileId, fileInfoService);

        try {
            // 时间间隔:60秒
            String cron = "0/60 * * * * ?";
            future = threadPoolTaskScheduler.schedule(
                    downloadFileRun, new CronTrigger(cron));

        } catch (Exception e) {
            log.error("定时任务启动过程失败:" + e.getMessage(), e);
        }
    }

    /**
     * 停止定时任务
     * @author jok
     * @date 2019/9/17 23:28
     */
    @Async
    public void stopDownloadFile()
    {
        if (future != null) {
            try {
                future.cancel(true);
                // 不一定必须用shutdown,看具体情况
                threadPoolTaskScheduler.shutdown();
            } catch (Exception e) {
                log.error("定时任务停止失败:" + e.getMessage(), e);
            }
        } else {
            log.info("定时任务已经停止!!!");
        }
    }


}

在Controller中,开启/停止定时任务:

@Controller
@Log4j2
public class DownloadController {
 
    @Autowired
    FileInfoService fileInfoService;

    /**
     * 开启定时任务:下载文件
     * @return org.springframework.web.servlet.ModelAndView
     * @author jok
     * @date 2019/9/17 22:15
     */
    @GetMapping("/start")
    public ModelAndView start() {
        try {
            log.info("开启定时下载 thread id :" + Thread.currentThread().getName());
            // 同时调用
            dynamicTimedTask.startDownloadFile(1, this.fileInfoService);
            dynamicTimedTask.startDownloadFile(2, this.fileInfoService);
            dynamicTimedTask.startDownloadFile(3, this.fileInfoService);
        } catch (Exception e) {
            log.error("定时下载任务过程错误:" + e.getMessage(), e);
        }

        return new ModelAndView("/downloading");
    }

    /**
     * 停止定时任务
     * @return org.springframework.web.servlet.ModelAndView
     * @author jok
     * @date 2019/9/17 22:15
     */
    @GetMapping("/stop")
    public ModelAndView stop() {
        try {
            log.info("停止定时下载 thread id :" + Thread.currentThread().getName());
            dynamicTimedTask.stopDownloadFile();
        } catch (Exception e) {
            log.error("停止定时下载任务过程错误:" + e.getMessage(), e);
        }

        return new ModelAndView("/stop");
    }
    
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值