一、数据库的锁以及乐观锁
1.共享锁
共享锁(S锁):共享 (S) 用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。
2.排他锁
排他锁(X锁):用于数据修改操作,例如 INSERT、UPDATE 或DELETE。确保不会同时同一资源进行多重更新。如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。
3.乐观锁
乐观锁:乐观锁不是数据库自带的,需要我们自己去实现。乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。
乐观锁的实现:在表中的数据进行操作时(更新),先给数据表加一个版本(version)字段,每操作一次,将那条记录的版本号加1。也就是先查询出那条记录,获取出version字段,如果要对那条记录进行操作(更新),则先判断此刻version的值是否与刚刚查询出来时的version的值相等,若相等,则说明这段期间,没有其他程序对其进行操作,则可以执行更新,将version字段的值加1;如果更新时发现此刻的version值与刚刚获取出来的version的值不相等,则说明这段期间已经有其他程序对其进行操作了,则不执行更新操作。
二、线程池的使用
1.线程池配置
@Configuration
@Async
public class ThreadPollConfig {
@Bean("taskExecutor")
public Executor asyncServiceExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//设置线程数量
executor.setCorePoolSize(5);
//设置线程池的最大容量
executor.setMaxPoolSize(20);
//设置等待队列的大小
executor.setQueueCapacity(Integer.MAX_VALUE);
//设置无任务是,线程的最大存活时间
executor.setKeepAliveSeconds(60);
//设置线程的默认名称
executor.setThreadNamePrefix("博客项目");
//任务执行完毕时才销毁线程
executor.setWaitForTasksToCompleteOnShutdown(true);
//线程池初始化
executor.initialize();
return executor;
}
}
2.使用
@Component
public class ThreadService {
/**
* 此方法在线程池中执行
* @param articleMapper mapper
* @param article 需要修改评论数量的文章
*/
@Async("taskExecutor")
public void updateArticleViewCount(ArticleMapper articleMapper, Article article) {
Integer viewCounts = article.getViewCounts();
Article articleUpdate = new Article();
articleUpdate.setViewCounts(viewCounts+1);
LambdaUpdateWrapper<Article> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(Article::getId,article.getId());
//设置一个相当于乐观锁
updateWrapper.eq(Article::getViewCounts,viewCounts);
articleMapper.update(articleUpdate,updateWrapper);
}
}
三、总结
使用线程池可以让给前端返回数据的接口不必等待评论数量更新操作完成后再执行,也不会受到更新异常的影响,提高了用户体验,同时提高了接口的性能。