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

2019 精帖
0 438

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层自己定义事务的开启和提交。
留言(0)
加载更多
猜你喜欢
  • file apache服器安装教

    <p class="MsoNormal"><span lang="EN-US">1.</span><span style="font-family: 宋体; mso-ascii-font-family
  • blog 阿里云服送邮件失败

    阿里云esc服器为了安全默认禁用25端口,所以导致 JavaMail 送邮件失败。错误代码:com.sun.mail.util.MailConnectException: Couldn't connect to host, port:
  • file 阿里巴巴Java手册-终极版.pdf

    <p><img src="http://www.jiajiajia.club:8089/weblog/2019_11_23/61ea8dbd-f630-4257-a12e-9c69a4673955.p
  • ofc java使用easypoi导出下载excel文件(maven)

    java使用easypoi导出下载excel文件(maven)
  • blog java微信开获取openid

    //访问的地址@RequestMapping("/transfer")public void transfer(HttpSession session,HttpServletRequest request,HttpServletRespon
  • blog 守护线

    Java分为两种线:用户线 和守护线 所谓守护线是指在序运行的时候在后台提供一种通用服的线,比如垃圾回收线就是一个很称职的守护者,且这种线不属于序中不或缺的部分。因 此,当所有的非守护线结束时,
  • blog java基础-译执行和解释执行

    概念: 解释运行: 一边对序进行翻译,翻译成计算机以执行的指令,一边交给计算机执行,翻译一句执行一句。 译运行: 对整个序先翻译成计算机解的指令,然后计算机以直接执行。java是解释执行还是译执
  • blog java动态译技术原分析

    1.动态译技术 从 JDK 1.6 开始引入了用 Java 代码重写的译器接口,使得我们以在运行时Java 源码,然后用类载器进行载,让 Java 语言更具灵活性,够完成许多高级的操作。 2.本次要实现的功 动态译和运
  • blog java集合TreeMap实现原

    java集合TreeMap实现原         TreeMap集合的实现其实说简单也简单说复杂也复杂,说简单是因为TreeMap底实现完全依靠红黑树这个数据结构,相比与HashMap来说Tr
  • blog 线的同步问题

    多线带来的问题: 线有时候回和其他线共享一些资源,比如内存、数据库等。当多个线同时读写同一份共享资源的时候,生冲突。这时候,我们就需要引入线“同步”机制,即各位线间要有顺序使用,不杂乱章随意使用。实例