04-悲观锁和乐观锁的区别和应用场景
作者:互联网
讲悲观锁和乐观锁之前,顺便复习一下同步锁和死锁
1、同步锁
- 同步锁是为了保证每个线程都能正常执行原子不可更改操作,同步监听"对象/同步锁/同步监听器/互斥锁"的一个标记锁
- 每个Java对象有且只有一个同步锁,在任何时刻,最多只允许一个线程拥有这把锁,当消费者线程试图执行以带有
synchronized(this)
标记的代码块的时候,消费者线程必须先取得this
关键字引用的Stack
对象的锁
2、死锁
-
线程死锁是指由于两个或者多个线程互相持有对方所需资源,导致这些线程处于等待状态,无法前往执行;当线程进入对象的
synchronized
代码块,便占有了资源,直到它退出该代码块或者调用wait
方法,才会释放资源,如果线程都不主动释放所占有的资源,将产生死锁 -
死锁的产生是必须要满足一些特定条件的
-
互斥条件
- 进程对于所分配的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放
-
请求和保持条件
- 一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放
-
不剥夺条件
- 任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用
-
循环等待条件
- 当发生死锁的时候,所等待的线程必定会形成一个环路(有线程获取锁的顺序是 A -> B,有的线程获取锁的顺序是 B -> A,形成一个环路),造成永久阻塞
-
3、悲观锁
- 悲观锁(Pessimistic Lock)总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿到这个数据就会阻塞直到它拿到锁
- 应用场景
- 传统的关系数据库里用到了很多这种锁机制,比如按使用性质划分的读锁,写锁和按作用范围划分的行锁,表锁。对于可能冲突的并发操作,以串行的方式取代并发执行,因而它也是一种悲观并发控制
4、乐观锁
- 乐观锁(Optimistic Lock)总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁;但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现版本号机制
- 实现方式
- 在数据表中假如一个数据版本号
version
字段,表示数据被修改的次数,当数据被修改的时候,version值会加一;当线程A要重新更新数据值的时候,在读取数据的时候也会读取version
值,在提交更新的时候,若刚才读取到的version
值与当前数据库中的version
值相等才更新,否则重新更新操作,直到更新成功
- 在数据表中假如一个数据版本号
- CAS算法
- CAS(Compare And Swap)比较并交换算法,是一种有名的无锁算法,在不适用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量同步,所以也叫非阻塞同步
- CAS算法设计到三个操作数
- ①、要更新的变量
v
- ②、预期的值
E
- ③、新值
N
- ①、要更新的变量
- 仅当
v
值等于E
值的时候,才会将v
的值设置为N
,否则说明都不做,最后CAS返回当前v
的值,CAS算法需要额外给出一个期望值,也就是个人认为现在变量应该是这样子,如果变量不是个人所期望的样子,就说明已经被别人修改过,就重新读取,再次尝试修改即可
5、乐观锁和悲观锁的应用场景
- 乐观锁使用于写比较少(即读多)的场景,即冲突很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但是如果是写比较多的情况,一般会经常发生冲突,这就会导致上层应用会不断的进行充实,这样反倒是降低了性能,所以一般多写的场景下用悲观锁比较合适
标签:同步,version,04,CAS,死锁,场景,线程,悲观 来源: https://www.cnblogs.com/OnlyOnYourself-lzw/p/16543498.html