赞
踩
本学期暑期项目实训,我们组开发的是选座小程序。在用户选座的过程中,必然要求如果多个用户同时选择了同一个座位,那只能有一个用户选座成功,数据库中只能插入一条选座信息,其他用户只能选座失败,收到座位已被选的提示。一开始对于这一部分限制只使用了@Transactional启用事务。但后来一想这样做只能保证在选座的过程中对数据库各表的增删改过程的原子性,而无法保证一个座位只能被一个用户选择。
后来在网上查找到这篇文章:
文章1:Synchronized锁在Spring事务管理下,为啥还线程不安全?
知道了可以利用Java synchronized关键字实现这一机制,synchronized关键字的使用方法为:
- 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
- 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
- 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
- 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
被synchronized关键字修饰的代码部分,一个时刻最多有一个线程能够访问,其他线程阻塞等待。synchronized修饰不同的对象时有细微的差别,具体请看上述引用的文章2。
因此,我在加了@Transactional注解的方法上加上了synchronized关键字,示例如下:
@Transactional
public synchronized void example() {
// 示例
}
但通过对文章1的学习发现,这样做并不能保证线程安全。原因是:线程1在执行example()前,事务被开启。在example()执行结束后,事务会被提交,而在这之间的一段时间内,线程2可以执行example(),因为线程1的事务还没有提交,所以线程2执行example()读到的数据是线程1事务执行前的数据,造成了脏读。
解决方法如下:不要将@Transactional和synchronized用在一个方法上,而是新建一个被synchronized修饰的方法调用被@Transactional调用的方法。示例如下:
@Transactional
public void example() {
// 示例
}
public synchronized void example() {
example();
}
这样在事务创建之前就已经保证了单线程执行,就不会出现上述问题了。
对于本项目要求的单机多线程情况下,该方法可以解决上述问题,但在分布式情况下就无法保证线程安全了,此时可以考虑分布式锁。
通过两篇文章的学习,我收获到了:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。