乐观锁
概念:
天生乐观 相信这个数据 除了我这个线程 没有其他的线程会去修改它 在尝试持有锁的时候 会取要修改对象的原值 在存储的时候 先查一下 看要改的值有没有更改 如果没有 就直接改 如果有了变化 就大骂一声 渣男 不是说好只有我的吗 然后告辞离去
CAS(Compare And Swap)是一种常见的“乐观锁”,大部分的CPU都有对应的汇编指令,它有三个操作数:内存地址V,旧值A,新值B。只有当前内存地址V上的值是A,B才会被写到V上,否则操作失败。
但实际我们 使用时 还是会判断一下 然后取了值再循环一次 直到获取了锁 才会推出循环 (其实这个是有一点自旋锁的概念的)
与之对应的是 java.util.concurrent.atomic 包下的AtomicInteger、AtomicReference等类
这个就是AtomicInteger类中关于添加的代码 valueOffset就是存储之前保存的值的
但是 乐观锁有时在遇到ABA问题时 就会束手无策 指你正准备原值为A的对象 这时 来了一个坏人 把这个值修改成了B 然后又来了另外一个捣蛋的 把值再改为A 这时 你看 诶 值还是A 那没事 我继续玩吧
针对这种情况,java并发包中提供了一个带有标记的原子引用类AtomicStampedReference,它可以通过控制变量值的版本来保证CAS的正确性。但其实很多场景下 大家并不关心 你中间改没改过 只要最后的结果就行了
自旋锁
概念:
来 让我去试试去获取一下锁
唉 获取不上
没关系 那我和自己玩一会吧
玩了一会后 再去尝试获取锁
还没好 那我再和自己玩一会吧
…
直到获取锁
看到这个概念 我们就可以想象 自旋锁 肯定适合一些业务处理较为快捷的同步代码块或者请求不是很频繁的(不频繁为啥不用单线程???)的地方使用 不然一个线程长时间占有锁 其他人一直和自己玩 即占着cpu资源 还啥都不干 这时要干啥 上班时间 聚众寒谝么 手头上没有其他工作么? 是不是工作不饱和
但是 这种状况也是有优点的就是 你在随时等着占有这个锁 就相当于 你一直保持着状态 随时准备投入工作 比那些 唉 还要等着啊 让我先去睡一觉 好了叫我的那些人 效率要更高更快和更好的 算是一种拿空间(cpu占用)换时间(效率)的锁
悲观锁
概念:
天生性格悲观 唉 肯定会有人来跟我抢锁的
所以我决定要先下手为强 只要被我拿到了这个锁
哼哼 你们谁都别想通过任何方式接触这个数据
读?想都不要想 写?做梦去吧
我感觉比起悲观锁 叫大佬锁 或者霸道锁更好
这一片数据 已经被我承包了
synchronized就是这么一种悲观锁 我在这干活的时候谁都不准过来 谁知要敢过来 门口乖乖排队去吧
重入锁
概念:
其实也可以称之为 递归锁
我要在这一块代码上加锁 诶 这怎么是个递归方法?
那往里递归的时候不是又要加锁么?
难道是在玩一种自己锁自己游戏吗?
当然不是 当你有这种疑问的时候 线程会拿出锁来给你看
你清醒清醒 看 就这一个锁 不要给自己疯狂加戏了
我是有这块代码的权限的 让让 我要去工作了
当走到最里层的时候 方法说 哈哈 我要释放锁了
看你们后面怎么玩 线程又会拿出锁来给你看
清醒清醒 琐不是你一个人的 等着吧 最后会释放的
Synchronized和ReentrantLock都是可重入锁
synchronized与Lock的区别
1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类;
2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
独享锁和共享锁
独享锁:
这块资源只要我线程A某某可以改
你们这些bcd线程加加读锁
欣赏一下我写的数据就好了
共享锁:
这一片花花草草 是我们大家的
我们看看就可以了 可不要去摘花 踩踏啊
独享锁和共享锁的概念其实就是 单写和多读的概念
只准一个人写 但是可以由多个人阅读(怎么感觉是在发博客)
读写锁
ReentrantReadWriteLock
ReadLock
WriteLock
实在么有劲继续加戏了
读写锁只需要在读操作时获取读锁,写操作时获取写锁即可。当写锁被获取到时,后续(非当前操作线程)的读写锁都会被阻塞,写锁释放之后,所有操作继续执行,编程方式相对于使用等待通知机制的实现方式而言。变得简单明了。
当线程持有写锁的时候 可以获取读锁 这个就是锁降级,而当读锁持有时 不能获取写锁 不允许锁升级
ReentrantReadWriteLock的特性
公平性选择 支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平
重进入 该锁支持重进入,以读写线程为例:读线程在获取了读锁之后,能够再次获取读锁。而写线程在获取了写锁之后能够再次获取写锁,同时也可以获取读锁
锁降级 允许从写锁降级为读锁,实现方式时:先获取写锁,再获取读锁,最后释放写锁,此时就会将为读锁。但是,从读锁升级到写锁时不可能的
Condition支持 写锁提供了一个Condition实现,对于写锁来说,该实现的行为与ReentrantLock.newCondition()提供的Condition实现对ReentrantLock所做的行为相同。当然,此Condition只能用于写锁。读锁不支持Condition,readLok().newCondition()会抛出UnsupportedOperationException。
监测 这些方法主要用于监听系统状态,而不是同步控制
分布式锁
在分布式框架下 进行锁的实现 这就不仅仅是线程之间锁可见了 需要进程之间都是可以见的
1、基于数据库实现
主键之类的
2、基于redis
a setnx()
setnx 的含义就是 SET if Not Exists,其主要有两个参数 setnx(key, value)。该方法是原子的,如果 key 不存在,则设置当前 key 成功,返回 1;如果当前 key 已经存在,则设置当前 key 失败,返回 0。
expire()
expire 设置过期时间,要注意的是 setnx 命令不能设置 key 的超时时间,只能通过 expire() 来对 key 设置。
3、基于ZooKeeper实现
https://www.cnblogs.com/seesun2012/p/9214653.html
后续进行学习!!!
学习自:
https://blog.csdn.net/qq_40094875/article/details/100928808
https://www.cnblogs.com/mmmmar/p/8624242.html
https://blog.csdn.net/u014034683/article/details/88749248
https://www.cnblogs.com/boothsun/p/7880949.html