oracle 文章:分解和合并:Java 也擅长轻松的并行编程!
简单例子 by Julien Ponge:
import java.util.*;
import java.util.concurrent.*;
import static java.util.Arrays.asList;
public class Sums {
static class Sum implements Callable<Long> {
private final long from;
private final long to;
Sum(long from, long to) {
this.from = from;
this.to = to;
}
@Override
public Long call() {
long acc = 0;
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());
}
}
}
作为新手使用,仿写简单类:
/**
* 文件下载工具类
* @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 = downloadFile.call();
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");
}
}