一、在application.yaml中进行多线程的配置
#多线程配置
task:
execution:
pool:
core-size: 20
max-size: 1000
keep-alive: 60000
queue-capacity: 1000
thread-name-prefix: ehub
配置相关参数解释:
core-size:核心线程数
max-size:最大线程数
keep-alive:空闲线程能存活的最长时间
queue-capacity:队列最大程度
thread-name-prefix:线程名
二、在启动类中加入@EnableAsync注解,允许开启异步
@EnableAsync
@SpringBootApplication
public class EhubApplication {
public static void main(String[] args) {
SpringApplication.run(EhubApplication.class, args);
}
}
三、service层添加对应接口,但是不能有返回值,如果需要返回值需要callback方法
public interface EhubService {
//下载阿里云OSS存储文件并进行分析后存入数据库中
void downOSS();
//打印文件与文件夹结果
void print() throws IOException;
}
四、在该service对应的实现类中使用@Async而不是使用@override,表示对于相对应的类或方法开启异步调用
@Service
public class EhubServiceImpl implements EhubService{
@Autowired
DocumentMapper documentMapper;
@Autowired
FolderMapper folderMapper;
// Endpoint为华东1(杭州)
public static String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
public static String accessKeyId = "LTAI5tRMrh1tg3inWY6nsBC5";
public static String accessKeySecret = "RYDOCkLifanjuXujtMnOscoOducCUO";
// Bucket名称
public static String bucketName = "56linked-test";
@Async
//下载阿里云OSS存储文件并进行分析后存入数据库中
public void downOSS(){
}
}
注意:在使用多线程时,一定要注意使用synchronized所标注的同步方法或同步代码块。当你使用了synchronized之后,即便你创建了线程池,各个线程也会等待其中一个线程执行同步方法或同步代码块之后才会执行。简而言之:多线程无效,无法提高效率。
解决方法:在本次项目中我使用了悲观锁(在SQL语句最后加入:for update)并使用编程式事务控制对每一个事务进行回滚,保证了数据的一致性。
//编程式事务
DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus transactionStatus = transactionManager.getTransaction(defaultTransactionDefinition);
try {
//判断数据库中是否有该文件夹(文件夹名相同且路径相同)
Folder folder = folderMapper.findFolderByName(name, path);
if (folder != null) {//如果有:更新文件夹大小,所含文件数+1;
folder.setFolderSize(folder.getFolderSize() + s.getSize());
folder.setFolderNumberOfFiles(folder.getFolderNumberOfFiles() + 1);
//之后查询该文件夹所含文件夹路径中是否含有文件夹完整路径,如果没有:所含文件夹数+1
String b[] = folder.getFolderPathOfFolders().split(",");
if (!Arrays.asList(b).contains(path1) && path1 != "最后一级文件夹") {
folder.setFolderNumberOfFolders(folder.getFolderNumberOfFolders() + 1);
//添加该目录
folder.setFolderPathOfFolders(folder.getFolderPathOfFolders() + "," + path1);
}
//更新至数据库
folderMapper.upDateFolder(folder);
} else { //如果没有(添加新文件夹):更新文件夹大小,所含文件数+1,添加所属路径。最后存入folder表中
//通过构造函数进行赋值
Folder folder1 = new Folder(name, s.getSize(), 1, 0, "", path);
//对path进行拆分,对每一级的文件文件夹所含文件夹数量加一
String b[] = path.split("/");
String path2 = "";
for (int j = 0; j < b.length; j++) {
if (j > 0) {
path2 = path2 + b[j - 1] + "/";
}
if (j < b.length - 1) {
//更新数据
Folder folder2 = folderMapper.findFolderByName(b[j], path2);
folder2.setFolderNumberOfFolders(folder2.getFolderNumberOfFolders() + 1);
//保存至数据库
folderMapper.deleteById(Math.toIntExact(folder2.getFolderId()));
folderMapper.insert(folder2);
}
}
//保存至数据库
folderMapper.insert(folder1);
}
transactionManager.commit(transactionStatus);
} catch (Exception e) {
log.error("xxxx", e);
transactionManager.rollback(transactionStatus);
}