怅然|关于java中的锁的理解


怅然|关于java中的锁的理解
怅然|关于java中的锁的理解Java 锁
乐观锁与悲观锁乐观锁和悲观锁都是用于解决并发场景下的数据竞争问题 , 但是却是两种完全不同的思想 。 它们的使用非常广泛 , 也不局限于某种编程语言或数据库 。
乐观锁的概念所谓的乐观锁 , 指的是在操作数据的时候非常乐观 , 乐观地认为别人不会同时修改数据 , 因此乐观锁不会上锁 , 只有在执行更新的时候才会去判断在此期间别人是否修改了数据 , 如果别人修改了数据则放弃操作 , 否则执行操作 。
怅然|关于java中的锁的理解悲观锁的概念所谓的悲观锁 , 指的是在操作数据的时候比较悲观 , 悲观地认为别人一定会同时修改数据 , 因此悲观锁在操作数据时是直接把数据上锁 , 直到操作完成之后才会释放锁 , 在上锁期间其他人不能操作数据 。
怅然|关于java中的锁的理解乐观锁的实现方式乐观锁的实现方式主要有两种 , 一种是CAS(Compare and Swap , 比较并交换)机制 , 一种是版本号机制 。
CAS机制【怅然|关于java中的锁的理解】CAS操作包括了三个操作数 , 分别是需要读取的内存位置(V)、进行比较的预期值(A)和拟写入的新值(B) , 操作逻辑是 , 如果内存位置V的值等于预期值A , 则将该位置更新为新值B , 否则不进行操作 。 另外 , 许多CAS操作都是自旋的 , 意思就是 , 如果操作不成功 , 就会一直重试 , 直到操作成功为止 。
版本号机制版本号机制的基本思路 , 是在数据中增加一个version字段用来表示该数据的版本号 , 每当数据被修改版本号就会加1 。 当某个线程查询数据的时候 , 会将该数据的版本号一起读取出来 , 之后在该线程需要更新该数据的时候 , 就将之前读取的版本号与当前版本号进行比较 , 如果一致 , 则执行操作 , 如果不一致 , 则放弃操作 。
悲观锁的实现方式悲观锁的实现方式也就是加锁 , 加锁既可以在代码层面(比如Java中的synchronized关键字) , 也可以在数据库层面(比如MySQL中的排他锁) 。
乐观锁与悲观锁优缺点和使用场景乐观锁和悲观锁并没有优劣之分 , 它们有各自适合的场景 。
功能限制乐观锁与悲观锁相比 , 适用的场景受到了更多的限制 , 无论是CAS机制还是版本号机制 。
比如 , CAS机制只能保证单个变量操作的原子性 , 当涉及到多个变量的时候 , CAS机制是无能为力的 , 而synchronized却可以通过对整个代码块进行加锁处理;再比如 , 版本号机制如果在查询数据的时候是针对表1 , 而更新数据的时候是针对表2 , 也很难通过简单的版本号来实现乐观锁 。
竞争激烈程度在竞争不激烈(出现并发冲突的概率比较小)的场景中 , 乐观锁更有优势 。 因为悲观锁会锁住代码块或数据 , 其他的线程无法同时访问 , 必须等待上一个线程释放锁才能进入操作 , 会影响并发的响应速度 。 另外 , 加锁和释放锁都需要消耗额外的系统资源 , 也会影响并发的处理速度 。
在竞争激烈(出现并发冲突的概率较大)的场景中 , 悲观锁则更有优势 。 因为乐观锁在执行更新的时候 , 可能会因为数据被反复修改而更新失败 , 进而不断重试 , 造成CPU资源的浪费 。


推荐阅读