目录
之前在学习redis的时候,做了一个“商品秒杀”的Demo公开到了网上,然后就有人评论:“redis本身就是线程安全的,没必要在秒杀方法上在加synchronized锁了”。
为了验证他的说法。
一开始我是打算用Junit,在test类中开多个线程同时去调用“doSecKill”方法。
但这种测试的方法行不通!在程序运行的过程中,涉及到redis的操作都直接跳过了。我查了一下网上的说法,发现Junit并不适合用来测试多线程程序。
可参考:Junit测试多线程_xiaoyangxavier的博客-CSDN博客
因为懒得装Jmeter,我就用电脑上现有的Postman测试了一下。
要测试的接口
Step1、
Step2、
Step3、上传参数文件(json)
Step4、点击“Run 并发测试”按钮
并发测试执行情况
Step1、Step2同2.1.1
Step3、上传参数文件(txt)
Step4、点击“Run 并发测试”按钮
并发测试执行情况
进入并发配置页面
Step1、准备参数文件 data.json
Step2、上传参数文件
Step3、点击“Run 并发测试”按钮
执行结果
Step1、准备参数文件 data.txt
Step2、上传参数文件
Step3、点击“Run 并发测试”按钮
执行结果
RedisTemplate类,在我们配置好redis之后,本身就是一个线程安全的类。再使用 RedisTemplate类里面的相关方法,比如:opsForValue、opsForValue().decrement等这些,就无需再在方法上加锁,或者给某个代码块加锁了。因为这些方法对redis的操作都是原子化的。
不过对于RedisTemplate类是如何做到线程安全的,我在网上没有查到相关的说明解释,自己看源码,只看到了RedisTemplate类引入了一个类:TransactionSynchronizationManager
RedisTemplate类中有一个excute()方法,负责与redis建立连接。采用工厂模式,由RedisConnectionFactory类统一创建线程安全的redis的连接对象。在使用过程中由org.springframework.transaction.support.TransactionSynchronizationManager类对每个分配出去的redis连接对象进行监听。TransactionSynchronizationManager使用ThreadLocal来为不同的事务线程提供独立的资源副本,并且同时维护这些事务的配置属性和运行状态。当TransactionSynchronizationManager监听到RedisTemplate在执行redis操作相关的方法时,比如:redisTemplate.opsForValue().get(kcKey)、redisTemplate.opsForSet().isMember(userKey, uid)、redisTemplate.opsForValue().decrement(kcKey)等等。对于每一次redis操作都会被TransactionSynchronizationManager监听并为其操作线程提供独立的资源副本,“独立的资源副本”使得每一次redis操作互不干涉,避免了“脏数据”的情况。
总言而之:
1、RedisTemplate类通过RedisConnectionFactory实现操作线程原子化,确保在并发的情况下每一个操作线程不会被其他线程干扰。
2、RedisTemplate类通过TransactionSynchronizationManager对每个操作线程在操作过程的redis里面的数据提供“独立的资源副本”,避免碰到“脏数据”。
注:以上关于redis的叙述仅供参考
Redis Setnx 命令_只有在 key 不存在时设置 key 的值。
https://www.jianshu.com/p/1e30e7a4b0c8
解决csv文件自动去掉首位0的方法__easyIT的博客-CSDN博客_csv保留首位是0
RedisTemplate中的execute方法如何使用 - 掘金 (juejin.cn)