Java 开启异步线程的几种方法,你都了解吗?
来自:blog.csdn.net/nhx900317/article/details/125478681
整体描述
实现方法
AsyncManager
的方式,如果需要复杂的线程操作,可以使用线程池实现。下面根据具体方法进行介绍。一、注解@Async
@Async
注解不能在类本身直接调用,在springboot框架中,可以使用单独的Service实现异步方法,然后在其他的类中调用该Service中的异步方法即可,具体如下:1. 添加注解
@EnableAsync
注解,开启异步线程功能package com.thcb.boot.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
/**
* MyConfig
*
* @author thcb
*/
@Configuration
@EnableAsync
public class MyConfig {
// 自己配置的Config
}
2. 创建异步方法Service和实现类
package com.thcb.execute.service;
import org.springframework.scheduling.annotation.Async;
/**
* IExecuteService
*
* @author thcb
*/
public interface IExecuteService {
/**
* 一些耗时的操作,使用单独线程处理
* 这里就简单写了一个sleep5秒的操作
*/
@Async
public void sleepingTest();
}
package com.thcb.execute.service.impl;
import com.thcb.execute.service.IExecuteService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
/**
* ExecuteService业务层处理
*
* @author thcb
*/
@Service
public class ExecuteServiceImpl implements IExecuteService {
private static final Logger log = LoggerFactory.getLogger(ExecuteServiceImpl.class);
@Override
public void sleepingTest() {
log.info("SleepingTest start");
try {
Thread.sleep(5000);
} catch (Exception e) {
log.error("SleepingTest:" + e.toString());
}
log.info("SleepingTest end");
}
}
3. 调用异步方法
package com.thcb.boot.controller;
import com.thcb.execute.service.IExecuteService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* TestController
*
* @author thcb
*/
@RestController
public class TestController {
private static final Logger log = LoggerFactory.getLogger(TestController.class);
@Autowired
private IExecuteService executeService;
@RequestMapping("/test")
public String test() {
return "spring boot";
}
@RequestMapping("/executeTask")
public String executeTask() {
log.info("executeTask Start!");
executeService.sleepingTest();
log.info("executeTask End!");
return "executeTask";
}
}
executeTask
,并log出executeTask End!
在5秒之后,log打出SleepingTest end
,说明使用了异步线程处理了executeService.sleepingTest
的方法。二、AsyncManager
AsyncManager
方法,也是SpringBoot框架中带的任务管理器,可以实现异步线程。1. 创建AsyncManager类
AsyncManager
首先需要创建一个AsyncManager
类,这个在springboot框架中应该也是有的:/**
* 异步任务管理器
*
* @author thcb
*/
public class AsyncManager {
/**
* 操作延迟10毫秒
*/
private final int OPERATE_DELAY_TIME = 10;
/**
* 异步操作任务调度线程池
*/
private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
/**
* 单例模式
*/
private AsyncManager() {
}
private static AsyncManager me = new AsyncManager();
public static AsyncManager me() {
return me;
}
/**
* 执行任务
*
* @param task 任务
*/
public void execute(TimerTask task) {
executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
}
/**
* 停止任务线程池
*/
public void shutdown() {
Threads.shutdownAndAwaitTermination(executor);
}
}
2. 创建一个耗时的操作类
public TimerTask sleepingTest() {
return new TimerTask() {
@Override
public void run() {
// 耗时操作
try {
Thread.sleep(5000);
} catch (Exception e) {
log.error("SleepingTest:" + e.toString());
}
}
};
}
3. 执行异步操作
// 异步线程池
AsyncManager.me().execute(sleepingTest());
三、线程池
1. 创建线程池
/**
* 线程池信息: 核心线程数量5,大数量10,队列大小20,超出核心线程数量的线程存活时间:30秒, 指定拒绝策略的
*/
private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(20), new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
log.error("有任务被拒绝执行了");
}
});
2. 创建一个耗时的操作类
/**
* 耗时操作
*/
static class MyTask implements Runnable {
private int taskNum;
public MyTask(int num) {
this.taskNum = num;
}
@Override
public void run() {
System.out.println("正在执行task " + taskNum);
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task " + taskNum + "执行完毕");
}
}
3. 执行线程池
for (int i = ; i < 20; i++) {
MyTask myTask = new MyTask(i);
threadPoolExecutor.execute(myTask);
System.out.println("线程池中线程数目:" + threadPoolExecutor.getPoolSize() + ",队列中等待执行的任务数目:" +
threadPoolExecutor.getQueue().size() + ",已执行完别的任务数目:" + threadPoolExecutor.getCompletedTaskCount());
}
threadPoolExecutor.shutdown();
总结
线程数量和cpu有关,使用线程时一定要注意线程的释放,否则会导致cpu线程数量耗尽; 使用注解完成的线程操作,不可以在自己的类中实现调用,因为注解后也是通过代理的方式完成异步线程的,好时在单独的一个service中写; 线程池好单独写,使用static和final修饰,保证所有使用该线程池的地方使用的是一个线程池,而不能每次都new一个线程池出来,每次都new一个就没有意义了。
CompletableFuture
,可以单独写一篇文章了,在此篇就不再介绍了:)相关文章