`
seya
  • 浏览: 360635 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

多线程并发下的Spring编程

 
阅读更多
事情是这样的,现在遇到一个场景,其实也大多数系统都会遇到的一个场景,就是购买机票的案例。 为了简化问题和保护公司信息安全,把事情简化为在数据库中有个数字,我每次要去读取并更新这个数字,每次加1. 初始值设为0.
设计表为
test {
num int(11) default 0;
}
采用ssm框架,controller层写个TestController
@Controller
public class TestController {
@Autowired
TestService testService;

@RequestMapping(value = "addNum")
@ResponseBody
public HttpRespMsg addNum(
HttpServletResponse response) throws Exception {
HttpRespMsg msg = null;
msg = testService.addNum();
return msg;
}
}
TestService定义如下:
public interface TestService {
public HttpRespMsg addNum();
}
@Transactional
@Service(value="testService")
public class TestServiceImpl implements TestService {
@Autowired
private TestMapper testMapper;

@Override
public HttpRespMsg addNum() {
HttpRespMsg msg = new HttpRespMsg();
TestExample tExp = new TestExample();
List<Test> list = testMapper.selectByExample(tExp);
if (list.size() == 0) {
Test t = new Test();
t.setNum(1);
testMapper.updateByExampleSelective(t, tExp);
} else {
int oldNum = list.get(0).getNum();
System.out.println(Thread.currentThread().getName() + "读取到oldNum=="+oldNum);
Test t = new Test();
t.setNum(oldNum+1);
testMapper.updateByExampleSelective(t, tExp);
}
msg.data = testMapper.selectByExample(tExp).get(0).getNum();

return msg;
}
}
很明显,在多线程模式下addNum会出现问题。 我用了JMeter模拟多线程并发访问,得到了验证。 20个请求,在1秒内同时调用addNum接口,结果数据库中的num字段值为6(有时候是7,8,5不确定)。问题出在读取和写入不是一个原子性操作。
那怎么解决呢?
首先想到@Transaction,不是有原子性特征吗?为什么不行呢,原来他的原子性指的是对数据库的操作失败的回滚,就是要么全部执行成功,要么都不成功。 并不能处理并发的问题。又想到了isolation隔离性,有READ_COMMITTED, READ_UNCOMMITTED, REPEATABLE_READ, SERIALIZABLE。具体含义此处不做解释,自行百度。 我尝试的结果是,前面三个都无效,最后一个会导致dead lock的异常产生,不能正常更新数据。 至此放弃这种方式。
我又尝试使用代码级别的锁, synchronized关键字来控制。
修改代码如下:
@Transactional
@Service(value="testService")
public class TestServiceImpl implements TestService {
@Autowired
private TestMapper testMapper;

@Override
public HttpRespMsg addNum() {
HttpRespMsg msg = new HttpRespMsg();
synchronized (TestService.class) {
TestExample tExp = new TestExample();
List<Test> list = testMapper.selectByExample(tExp);
if (list.size() == 0) {
Test t = new Test();
t.setNum(1);
testMapper.updateByExampleSelective(t, tExp);
} else {
int oldNum = list.get(0).getNum();
System.out.println(Thread.currentThread().getName() + "读取到oldNum=="+oldNum);
Test t = new Test();
t.setNum(oldNum+1);
testMapper.updateByExampleSelective(t, tExp);
}
msg.data = testMapper.selectByExample(tExp).get(0).getNum();

}
return msg;
}
}
以为这样是可以的了。赶紧验证下。 重置数据库的num为0,再跑20个同步线程。结果发现num变成了16,有进步,贴近了。但是为什么不是20呢。经过思考和调试,发现线程的锁是起效的,但是在读取的时候,依然会有读取不到最新数据的情况,结合Transaction的特点,一拍脑袋想起来,原来我们的事务是修饰到函数上的,整个函数执行完了才会提交到数据库。 而我们的锁是加载函数里面的代码块上的,范围小于事务。所以同步的代码执行完了,但是数据并没有立马提交。 导致下一个线程读取的时候依旧是老数据。
好吧,那我把synchronized加到addNum上吧。
public synchronized HttpRespMsg addNum(),
接口定义和实现里面都这样改一下,嗯编译没问题。跑一下试试看, opppps. Spring报错了。
修饰符有问题,Spring不允许在service里面的函数前面加这个关键字。只能用public修饰。
最后没办法,加到TestController里的addNum上吧。
@RequestMapping(value = "addNum")
@ResponseBody
public synchronized HttpRespMsg addNum(
HttpServletResponse response) throws Exception {
HttpRespMsg msg = null;
msg = testService.addNum();
return msg;
}
赶紧测试下。
成功!
然而这样会有性能问题,并发时,不能同时执行。是否可以有数据库的存储过程,手动给表加锁等方式解决,有待探索。
分享到:
评论

相关推荐

    多线程并发学习书籍

    在IT行业中,多线程并发编程是至关重要的一个领域,特别是在服务器端开发、大数据处理以及高性能计算中。这里提到的“多线程并发学习书籍”集合包含六本关于这一主题的专业书籍,覆盖了2012年至2018年的最新知识。...

    JAVA零基础到高级进阶特训营 JAVA多线程并发设计+Spring高级+数据库开发+JAVA基础等

    这套课程既可以作为从零基础开始...课程的主要内容涉及有JAVA基础课程、JAVA多线程与并发编程、数据库开发基础和进阶、Spring Framework、Spring进阶、Spring MVC框架、Spring boot、Java常用类库、Java异常处理等等

    Spring的多线程应用

    在【标题】"Spring的多线程应用"中,我们关注的是Spring如何支持和管理多线程,这是现代并发编程的一个关键特性。在【描述】中提到的"一个简单的spring的多线程demo",我们可以理解为一个示例项目,旨在帮助开发者...

    spring boot中多线程开发的注意事项总结

    Spring Boot通过`TaskExecutor`接口提供了一种方便的方式来实现多线程和并发编程。`TaskExecutor`允许我们定义一个线程池,有效地管理和调度线程资源。本文将深入探讨在Spring Boot中使用多线程开发需要注意的关键点...

    架构师学习线路图,详细介绍java学习线路图,包括java多线程并发,JVM,spring,springboot,s.zip

    1. **Java多线程并发**:在Java中,多线程并发是性能优化的关键。它允许程序同时执行多个任务,提高系统的资源利用率。Java提供了多种创建和管理线程的方式,如Thread类、Runnable接口、Executor框架等。理解线程...

    java中spring里实现多线程

    此外,Spring的`@Scope`注解可以帮助管理单例bean在多线程环境下的状态,避免数据不一致。 总之,Spring通过提供高级的线程管理和调度工具,简化了在Java应用程序中实现多线程的复杂性。理解并熟练使用这些工具,能...

    Java面试题、JVM面试题、多线程面试题、并发编程、设计模式面试题

    Java面试题、JVM面试题、多线程面试题、并发编程、设计模式面试题、SpringBoot面试题、SpringCloud面试题、MyBatis面试题、Mysql面试题、VUE面试题、算法面试题、运维面试题。 收集汇总各行业笔试or编程题解题思路 ...

    spring 多线程队列执行

    在Spring框架中,多线程队列执行是一个重要的性能优化策略,它可以帮助应用程序更高效地处理并发任务,尤其是在高负载和大数据量的场景下。本文将深入探讨Spring如何实现多线程队列以及其相关的核心概念和技术。 1....

    java多线程处理数据库数据

    1. **线程安全**:由于多线程环境下可能存在数据竞争,所以在访问共享资源(如数据库连接)时,需要确保线程安全。可以使用`synchronized`关键字或者`Lock`来同步访问。 2. **事务管理**:在多线程环境中,可能需要...

    spring4+junit4.8 +多线程TheadTool

    而多线程在现代编程中扮演着关键角色,特别是在处理并发任务、提高系统效率时。 在"spring4+junit4.8 +多线程TheadTool"的场景下,我们可以深入探讨以下几个知识点: 1. **Spring4框架**:Spring4在Spring3的基础...

    Spring Boot多线程demo

    在 Java 中,多线程编程是非常重要的一种技术,通过使用多线程可以提高程序的并发性和性能。Spring Boot 提供了多种方式来实现多线程编程,其中最简单的方式就是使用 @Async 注解。 使用 @Async 注解 要使用 @...

    maven管理的Spring多线程任务demo

    在SSM(Spring、SpringMVC、MyBatis)框架背景下,如果你已经有所了解,那么这个例子将帮助你进一步理解Spring框架中的多线程处理。 首先,Maven是基于项目对象模型(Project Object Model,POM)的概念,通过XML...

    多线程及spring相关面试专题及答案.zip

    6. **Spring事务管理**:Spring提供了声明式和编程式的事务管理,确保在多线程环境中数据的一致性。 7. **Spring Bean**:Spring容器管理的对象称为Bean,容器通过XML、注解或Java配置来定义Bean的生命周期和装配...

    使用java的HttpClient实现多线程并发

    为了实现多线程并发,我们需要创建一个`Runnable`任务,该任务用于执行HTTP GET请求: ```java public class GetTask implements Runnable { private final String url; public GetTask(String url) { this.url ...

    软件工程师的学习笔记,包含网络、操作系统、设计模式、JVM、多线程与高并发、Spring、MySQL......zip

    这篇学习笔记涵盖了软件工程师在职业发展中需要掌握的关键领域,包括网络、操作系统、设计模式、Java虚拟机(JVM)、多线程与高并发处理、Spring框架以及MySQL数据库。以下是对这些知识点的详细解读: 1. **网络**...

    2023 java并发编程手册

    Java 并发编程是指在 Java 语言中编写多线程程序的技术,旨在提高程序的执行效率和响应速度。Java 并发编程手册是 Java 开发者必备的知识储备,以下是 Java 并发编程的关键知识点: 1. Java 内存模型(JMM) Java ...

    springboot多线程demo

    - **并发编程**:多线程是并发编程的一种方式,它允许多个任务在同一时间执行,从而提高程序的执行效率。在Java中,可以通过`Thread`类或者实现`Runnable`接口来创建线程。 - **线程池**:为了避免频繁地创建和...

    2022java面试题、JVM面试题、多线程面试题、并发编程

    Java前后开发面试题,大厂进阶之路,基于JavaGuide、Cyc大佬、牛客...包含计算机网络知识、JavaSE、JVM、Spring、Springboot、SpringCloud、Mybatis、多线程并发、netty、MySQL、MongoDB、Elasticsearch、Redis、HBASE

    Springboot Druid多数据源 多线程

    对于初学者,这是一个很好的学习资源,可以深入理解Spring Boot的自动配置、Druid的数据源管理和Java的多线程编程。同时,对于有经验的开发者,这个项目也可以作为一个基础,进一步扩展到更复杂的数据库同步和分布式...

    202面试题,Java面试题、JVM面试题、多线程面试题、并发编程

    Java前后开发面试题,大厂进阶之路,基于JavaGuide、Cyc大佬、牛客...包含计算机网络知识、JavaSE、JVM、Spring、Springboot、SpringCloud、Mybatis、多线程并发、netty、MySQL、MongoDB、Elasticsearch、Redis、HBASE

Global site tag (gtag.js) - Google Analytics