java并发编程之 service层处理并发事务加锁可能会无效

硅谷探秘者 3921 0 0

java并发编程之 service层处理并发事务加锁可能会无效

        最近注意到一个问题--在service层处理要多次操作数据库事务时往往要@Transactional事务注解,这个时候就要注意了,如果是在并发情况下,而且在service层加了锁,这个时候并不能保证这个事务操作的原子性,并会出现我们意向不到的问题。

        本人做了一个测试,service层有一个方法,方法中获取数据库中的一个int值,然后将这个值自增后存入数据库,例如下面两段代码:(注意方法上加了@Transactional事务注解)

@Service
public class TestService {
	@Autowired
	private TestMapper testMapper;
	
	@Transactional
	public synchronized void test() {
		int i=testMapper.get();
		testMapper.add(++i);
	}
}

获下方的写法,都是有问题的。

@Service
public class TestService {
	private static final Object obj = new Object();
	@Autowired
	private TestMapper testMapper;
	@Transactional
	public void test() {
		synchronized (obj) {
			int i=testMapper.get();
			testMapper.add(++i);
		}
	}
}

        为了解决并发问题上述两段代码都用了不同的形式加了锁,可能大部分初级程序员都会觉得上述代码没有什么问题。但实际是两段代码都有问题。测试的时候用的是jmeter工具,用2000个线程同时访问模拟并发,经过多次测试后数据库中的值都小于2000。

那么和我们的想象不一致的原因是什么呢?

        众所周知,我们在spring中使用@Transactional事务注解,那么这个事务的开启和提交是spring利用aop帮我们自动完成的。说白了就是执行方法之前spring帮我们开启事务,方法执行完毕后spring再帮我们把事务提交。而方法的执行和事务的开启及提交并不是一个原子操作。所以方法执行完毕以后事务并没有提交。所以我们无论是将synchronized关键字加再方法体上还是用代码块的方式,只能保证方法中的代码或synchronized代码块中的代码执行的原子性。所以在高并发的情况下,就极有可能出现一些线程service方法已经执行完毕或synchronized代码块已经执行完,但是事务还没有提交(数据库中的值并没有改变),另一些线程就开始读取数据库中的值(没有提交事务之前的值)那么就有可能多个线程读取的是同一个值。所以就会出现和我们预想的结果不一致的情况。所以在并发程序中,而且还牵扯到事务的情况下,要特别注意这一点。

解决办法
  1. 在controller层加锁。
  2. 在service层自己定义事务的开启和提交。

评论区
请写下您的评论...
暂无评论...
猜你喜欢
java基础 1747 java-解CAS算法1.什么是cas算法?CAS:CompareandSwap,即比较再交换。jdk5增包java.util.concurrent.*,其下面的类使用CAS算法实
java虚拟机(jvm) 1701 的实现也有相当大的参考意义。“让计算机执行若干个运算任”与“更充分地利用计算机器的间的因果关系,看起来顺成章,实际上它们间的关系没有想象中的那么简单,其中一个重要的复杂性来源是绝
java基础 1910 1.先看一下线的生命周期转换图(学java的此图必背)本篇文章的主要目的不是分析线的各种状态间的转换,而主要是研究一下线间的通讯机制,以及Object的wait方法和notify方法。所以
java基础 1508 java线通讯生产者消费者模式生产者消费者模式是、多线中经典的设计模式,生产者和消费者通过分离的执行工作解耦,简化了开模式,生产者和消费者以以不同的速度生产和消费数据。一个生产和消
official 1114 ,这样不同时行地各个进机调度,就是从就绪队列中按照一定的算法选择一个进机分配给它运行,以实现进执行。高级调度由于内存空间有限,有时法将用户提交的作业全部放入内存,因此
java基础 3113 为什么用线池:1.创建/销毁线伴随着系统开销,过于频繁的创建/销毁线很大度上影响率2.线数量过多,抢占系统资源从而导致阻塞3.对线进行一些简单的管
java基础 3751 通过一些更高率的等价方式去实现它。三、Java虚拟机栈  与序计数器一样,Java虚拟机栈(JavaVirtualMachineStack)也是线私有的,它的生命周期与线相同。虚拟机栈
java基础,springboot 1736 打成一个jar包,然后部署到服器上(这里只讨论Linux,因为没多少人拿Windows当服器)。nohup命令以让序作为后台进执行,但是它不好管维护,也显得很不专业。更好的方法是将
归档
2018-11  12 2018-12  33 2019-01  28 2019-02  28 2019-03  32 2019-04  27 2019-05  33 2019-06  6 2019-07  12 2019-08  12 2019-09  21 2019-10  8 2019-11  15 2019-12  25 2020-01  9 2020-02  5 2020-03  16 2020-04  4 2020-06  1 2020-07  7 2020-08  13 2020-09  9 2020-10  5 2020-12  3 2021-01  1 2021-02  5 2021-03  7 2021-04  4 2021-05  4 2021-06  1 2021-07  7 2021-08  2 2021-09  8 2021-10  9 2021-11  16 2021-12  14 2022-01  7 2022-05  1 2022-08  3 2022-09  2 2022-10  2 2022-12  5 2023-01  3 2023-02  1 2023-03  4 2023-04  2 2023-06  3 2023-07  4 2023-08  1 2023-10  1 2024-02  1 2024-03  1 2024-04  1 2024-08  1
标签
算法基础 linux 前端 c++ 数据结构 框架 数据库 计算机基础 储备知识 java基础 ASM 其他 深入理解java虚拟机 nginx git 消息中间件 搜索 maven redis docker dubbo vue 导入导出 软件使用 idea插件 协议 无聊的知识 jenkins springboot mqtt协议 keepalived minio mysql ensp 网络基础 xxl-job rabbitmq haproxy srs 音视频 webrtc javascript 加密算法
目录
没有一个冬天不可逾越,没有一个春天不会来临。最慢的步伐不是跬步,而是徘徊,最快的脚步不是冲刺,而是坚持。