当前位置:   article > 正文

Synchronized锁在Spring事务管理下,为啥还线程不安全(1)

Synchronized锁在Spring事务管理下,为啥还线程不安全(1)

@RestController

public class EmployeeController {

@Autowired

private EmployeeService employeeService;

@RequestMapping(“/add”)

public void addEmployee() {

for (int i = 0; i < 1000; i++) {

new Thread(() -> employeeService.addEmployee()).start();

}

}

}

@Service

public class EmployeeService {

@Autowired

private EmployeeRepository employeeRepository;

@Transactional

public synchronized void addEmployee() {

// 查出ID为8的记录,然后每次将年龄增加一

Employee employee = employeeRepository.getOne(8);

System.out.println(employee);

Integer age = employee.getAge();

employee.setAge(age + 1);

employeeRepository.save(employee);

}

}




简单地打印了每次拿到的employee值,并且拿到了SQL执行的顺序,如下(贴出小部分):



![搞不懂,Synchronized锁在Spring事务管理下,为啥还线程不安全?](https://imgconvert.csdnimg.cn/aHR0cDovL3AxLnBzdGF0cC5jb20vbGFyZ2UvcGdjLWltYWdlLzc2ODVhMTljZDZjYjQ3YjE4ZGEzODVlOTBhZWU4NGM5?x-oss-process=image/format,png)



从打印的情况我们可以得出:多线程情况下并**没有串行**执行addEmployee()方法。这就导致对同一个值做**重复**的修改,所以最终的数值比1000要少。



二、图解出现的原因

=========



发现并不是**同步**执行的,于是我就怀疑synchronized关键字和Spring肯定有点冲突。于是根据这两个关键字搜了一下,找到了问题所在。



我们知道Spring事务的底层是Spring AOP,而Spring AOP的底层是动态代理技术。跟大家一起回顾一下动态代理:



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

public static void main(String[] args) {

// 目标对象

Object target ;

Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), Main.class, new InvocationHandler() {

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

// 但凡带有@Transcational注解的方法都会被拦截

// 1… 开启事务

method.invoke(target);

// 2… 提交事务

return null;

}

});

}




(详细请参考我之前写过的动态代理:给女朋友讲解什么是代理模式)



实际上Spring做的处理跟以上的思路是一样的,我们可以看一下TransactionAspectSupport类中invokeWithinTransaction():



![搞不懂,Synchronized锁在Spring事务管理下,为啥还线程不安全?](https://imgconvert.csdnimg.cn/aHR0cDovL3AxLnBzdGF0cC5jb20vbGFyZ2UvcGdjLWltYWdlLzY1MjY1YjMwYTExYTRkMTNhYzVhNzg4YTQ4NjE5YzNi?x-oss-process=image/format,png)



调用方法**前**开启事务,调用方法**后**提交事务



![搞不懂,Synchronized锁在Spring事务管理下,为啥还线程不安全?](https://imgconvert.csdnimg.cn/aHR0cDovL3A5LnBzdGF0cC5jb20vbGFyZ2UvcGdjLWltYWdlLzc4NGQ0OGIwZDJhOTQwNDZiMDAzZTZiMWUwZWNlNDZm?x-oss-process=image/format,png)



在多线程环境下,就可能会出现:**方法执行完了(synchronized代码块执行完了),事务还没提交,别的线程可以进入被synchronized修饰的方法,再读取的时候,读到的是还没提交事务的数据,这个数据不是最新的**,所以就出现了这个问题。



![搞不懂,Synchronized锁在Spring事务管理下,为啥还线程不安全?](https://imgconvert.csdnimg.cn/aHR0cDovL3AxLnBzdGF0cC5jb20vbGFyZ2UvcGdjLWltYWdlLzY4YmE2YmFhMDkzNzQ3ZGJiM2RjMTI2MTMyNTU1MGFk?x-oss-process=image/format,png)



三、解决问题

======



从上面我们可以发现,问题所在是因为@Transcational注解和synchronized一起使用了,**加锁的范围没有包括到整个事务**。所以我们可以这样做:



新建一个名叫SynchronizedService类,让其去调用addEmployee()方法,整个代码如下:



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

@RestController

public class EmployeeController {

@Autowired

private SynchronizedService synchronizedService ;

@RequestMapping(“/add”)

public void addEmployee() {

for (int i = 0; i < 1000; i++) {

new Thread(() -> synchronizedService.synchronizedAddEmployee()).start();

}

}

}

// 新建的Service类

@Service

public class SynchronizedService {

@Autowired

private EmployeeService employeeService ;

// 同步

public synchronized void synchronizedAddEmployee() {

employeeService.addEmployee();

}

}

@Service

public class EmployeeService {

@Autowired

private EmployeeRepository employeeRepository;

@Transactional

public void addEmployee() {

// 查出ID为8的记录,然后每次将年龄增加一

Employee employee = employeeRepository.getOne(8);

System.out.println(Thread.currentThread().getName() + employee);

Integer age = employee.getAge();

employee.setAge(age + 1);

employeeRepository.save(employee);

}

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数网络安全工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上网络安全知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注网络安全获取)
img

本人从事网路安全工作12年,曾在2个大厂工作过,安全服务、售后服务、售前、攻防比赛、安全讲师、销售经理等职位都做过,对这个行业了解比较全面。

最近遍览了各种网络安全类的文章,内容参差不齐,其中不伐有大佬倾力教学,也有各种不良机构浑水摸鱼,在收到几条私信,发现大家对一套完整的系统的网络安全从学习路线到学习资料,甚至是工具有着不小的需求。

最后,我将这部分内容融会贯通成了一套282G的网络安全资料包,所有类目条理清晰,知识点层层递进,需要的小伙伴可以点击下方小卡片领取哦!下面就开始进入正题,如何从一个萌新一步一步进入网络安全行业。

学习路线图

其中最为瞩目也是最为基础的就是网络安全学习路线图,这里我给大家分享一份打磨了3个月,已经更新到4.0版本的网络安全学习路线图。

相比起繁琐的文字,还是生动的视频教程更加适合零基础的同学们学习,这里也是整理了一份与上述学习路线一一对应的网络安全视频教程。

网络安全工具箱

当然,当你入门之后,仅仅是视频教程已经不能满足你的需求了,你肯定需要学习各种工具的使用以及大量的实战项目,这里也分享一份我自己整理的网络安全入门工具以及使用教程和实战。

项目实战

最后就是项目实战,这里带来的是SRC资料&HW资料,毕竟实战是检验真理的唯一标准嘛~

面试题

归根结底,我们的最终目的都是为了就业,所以这份结合了多位朋友的亲身经验打磨的面试题合集你绝对不能错过!

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img

~

面试题

归根结底,我们的最终目的都是为了就业,所以这份结合了多位朋友的亲身经验打磨的面试题合集你绝对不能错过!

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-vPORBtR8-1712862545371)]

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/620958
推荐阅读
相关标签
  

闽ICP备14008679号