并发出现的情况
情况一:CPU调度导致
这种情况,相当于CPU切断了我的代码,局部变量,线程安全,但这个变量可能是从数据库,缓存等地方读取出来的,读取的数据已经因为CPU调度的问题,已经被其他的线程更改掉了。个人觉得这种情况是不可重复读的概念,导致的并发问题
这不仅仅是多线程可能出现的问题,就算我是单线程,处理延迟也可能导致。
例如:我读取库存数量为1,10秒钟后,我再去减掉库存。但是我手动更改了数据表,库存变成了0,10秒钟,库存被程序执行了-1操作照样导致了问题的产生。这个时候从JVM层面就处理不了,需要通过mysql的锁操作
CPU的调度时间片可能会把代码切割成几片进行执行
例如切成了两片,下一片的的数据需要使用上一片读取处理的共享数据
在这期间,有其他的线程过来,改掉了这个数据
下一片代码片执行的时候,共享变量已经被其他线程改变了,导致结果与预期不一致
情况二:共享变量读取与修改
也是CPU调度了,但是属于JVM层面的并发,共享变量数据被改掉了。A线程读取,B线程改掉了共享变量值,A不知道继续使用读取的数据。导致结果异常
情况三:共享变量在工作内存与核心内存的处理
这种情况,又有点区别,虽然也是不可以重复读,但并不是我自己写代码的读取与修改,而是站在内存方面,修改的共享变量的时候,需要先去读取数据然后再更改。还是读取与修改两个操作不是原子性的。
我需要修改某一个变量,例如:hashmap,我虽然是只有写入的代码,但是从内存的角度来看,内存是需要先把变量从核心内存读入工作内存中,然后工作内存里面操作完毕后,再放回去。 这个是其他的线程已经改过了这个数据,你修改完毕后放回去,相当于把其他线程辛辛苦苦修改的给覆盖掉了
总结
- 在学习锁的时候,要考虑的并不是单纯的从JVM层面去锁定代码或者数据库层面去考虑数据库锁,而是要分情况。
- 本质都是不可以重复读取,那么我在读的时候跟用的时候,必须达到原子性,也就是说读取跟使用要锁到一起。
使用
那么基于总结,我什么时候加锁呢?
-
碰到共享变量(类变量,实例变量),考虑加锁
-
代码中存在共享变量的读取与使用,考虑锁
锁方案:读取跟使用这一段代码锁起来。
单个JVM使用代码锁(synchronized,reentrantLock)
多个JVM 考虑第三方介入(例如:redis的单线程,一次只能一个线程来执行代码) -
对共享变量进行增删改,考虑锁
锁方案:- 使用安全类型(hashmap 改为 concurrentHashMap)
- 操作的时候用代码锁起来,也是让它每次只能被一个线程操作。
-
操作的变量不是共享变量,但数据来源于第三方(数据库),读取后要需要去使用它
锁方案:读取与使用的过程中锁起来。- 可以考虑数据库锁,读取的时候锁起来这个数据,不能进行更改,使用完毕后释放锁 这里我们只讨论锁的使用情况,乐观锁属于锁优化,所以不考虑乐观锁方案
- 如果没有其他地方会更改,使用代码锁也行。
如有理解不到位,请指正
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/181708.html