|
在Java项目中,我们经常会遇到多线程并发问题,尤其是在生成仅有标识符的场景中。本文将结合际项目案例,详细讲解如何使用synchronized解决多线程并发导致的任务编号重复问题。
问题背景
在我们的项目中,有一个创建任务的功能。每次创建任务时,系统会根据当前时间(年月日时分秒)生成一个任务编号,代码如下:
StringtaskNum=LocalDateTimenow()format(DateTimeFormatterofPattern("yyyyMMddHHmmss"));
理论上,每次生成的任务编号应该是仅有的。然而,在高并发场景下,多个线程同时调用该接口时,可能会生成相同的任务编号,这显然是不合理的。如图
问题复现
为了验证问题,我添加了输出开始时间和结束时间,并且精确到毫秒,因为这个接口响应时间在100~300毫秒,如果精确到秒,出现在同一时间那是必然的,看如果是毫秒级别,能否就直接解决这个问题了,代码类似如下
**
??*创建任务-提交
??*
??*@paramdto创建任务提交的数据对象
??*@return
??*
??@Override
??@Transactional
??publicintaddTask(TaskCreateDTOdto,StringuserId){
Systemoutprintln("开始"+LocalDateTimenow()format(DateTimeFormatterofPattern("yyyyMMddHHmmssSSS")));
?
StringtaskNum=LocalDateTimenow()format(DateTimeFormatterofPattern("yyyyMMddHHmmssSSS"));
Systemoutprintln("任务编号:"+taskNum);
一些业务逻辑
Systemoutprintln("结束"+LocalDateTimenow()format(DateTimeFormatterofPattern("yyyyMMddHHmmssSSS")));
?
return1;
?}
启动项目,并用APIfox进行并发测试,如图可以看到,即使精确到毫秒级,还是会生成出相同的编号,这说明在高并发场景下,多个线程同时进入了该方法。
问题分析
问题的根本原因是多个线程在同一时间调用了该方法,导致生成的任务编号相同。为了解决这个问题,我们需要确保同一时间只有一个线程可以执行该方法。
解决方案
在Java中,synchronized关键字可以确保同一时间只有一个线程执行被修饰的方法。但需要注意的是,synchronized的有效性依赖于方法所在类的例作用域。
单例模式:如果类的作用域是单例(Spring默认),则可以直接使用synchronized修饰方法。
多例模式:如果类的作用域是多例,则需要额外处理(例如使用类锁或静态锁)。
在我们的项目中,通过输出thishashCode()验证了当前类是单例的:
Systemoutprintln("当前例哈希码:"+thishashCode());
如上图,可以看到,每此在方法中输出的this的哈希码都是一样的,表示当前的现类对象在整个程序中是单例的,因此直接在方法上加上synchronized即可解决此问题
验证结果
代码如下
@Override
??@Transactional
??publicsynchronizedintaddTask(TaskCreateDTOdto,StringuserId){
Systemoutprintln("开始"+LocalDateTimenow()format(DateTimeFormatterofPattern("yyyyMMddHHmmssSSS")));
?
StringtaskNum=LocalDateTimenow()format(DateTimeFormatterofPattern("yyyyMMddHHmmssSSS"));
Systemoutprintln("任务编号:"+taskNum);
一些业务逻辑
Systemoutprintln("结束"+LocalDateTimenow()format(DateTimeFormatterofPattern("yyyyMMddHHmmssSSS")));
?
return1;
?}
再次使用APIfox进行并发测试,结果如下:
每次生成的任务编号都是仅有的。
多个线程依次执行该方法,避免了并发问题。
如图
注意事项
虽然synchronized可以有效解决并发问题,但在高并发场景下,可能会带来性能瓶颈。因此,建议在以下情况下使用:
并发量较低:适合并发量较小的场景。
方法执行时间较短:适合执行时间较短的方法。
需要严格保证线程安全:适合对数据一致性要求较高的场景。
如果并发量较大或方法执行时间较长,可以考虑其他化方案,例如减小锁粒度、使用读写锁或锁编程。
结语
通过幽络源本文的案例,我们深入了解了如何使用synchronized解决多线程并发问题。如果你对Java多线程编程感兴趣,欢迎加入我们的学习交流群(Q群:307531422),获取更多源码、技术和创教程。
脚踏实地的对源码网进行深入研究是追求发展的唯一办法。提供经过严格测试的免费源码、各种线上兼职和网络兼职的网创教程、编程及网络相关的技术教程分享,助您轻松获取资源和技术支持。https://www.youluoyuan.com/
|
|