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

硅谷探秘者 1417 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基础 497 java-解CAS算法1.什么是cas算法?CAS:CompareandSwap,即比较再交换。jdk5增包java.util.concurrent.*,其下面的类使用CAS算法实
java虚拟机(jvm) 449 的实现也有相当大的参考意义。“让计算机执行若干个运算任”与“更充分地利用计算机器的间的因果关系,看起来顺成章,实际上它们间的关系没有想象中的那么简单,其中一个重要的复杂性来源是绝
java基础 502 1.先看一下线的生命周期转换图(学java的此图必背)本篇文章的主要目的不是分析线的各种状态间的转换,而主要是研究一下线间的通讯机制,以及Object的wait方法和notify方法。所以
java基础 325 java线通讯生产者消费者模式生产者消费者模式是、多线中经典的设计模式,生产者和消费者通过分离的执行工作解耦,简化了开模式,生产者和消费者以以不同的速度生产和消费数据。一个生产和消
official 127 ,这样不同时行地各个进机调度,就是从就绪队列中按照一定的算法选择一个进机分配给它运行,以实现进执行。高级调度由于内存空间有限,有时法将用户提交的作业全部放入内存,因此
java基础 2211 为什么用线池:1.创建/销毁线伴随着系统开销,过于频繁的创建/销毁线很大度上影响率2.线数量过多,抢占系统资源从而导致阻塞3.对线进行一些简单的管
official 12 消息送确认在使用mq送消息的时候,由于一些不确定因素,导致消息送失败,比如网络的问题,服器问题,或mq本身的问题都导致消息送失败。那么当消息送成功或失败后序如何感知呢?那就
java基础 2094 。2.syncrhoized的线的Object类的wait()/notify()/notifyAll()1)wait()、notify()和notifyAll()方法是本地方法,且为final方法
归档
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
标签
算法基础 linux 前端 c++ 数据结构 框架 数据库 计算机基础 储备知识 java基础 ASM 其他 深入理解java虚拟机 nginx git 消息中间件 搜索 maven redis docker dubbo
目录