本篇计划总结一下,Java中最基本的锁,以及springboot分布式锁的redis实现
1 锁 一般是抽象的概念,Java中有具体的对象和关键字,来实现这些锁,下面介绍这些锁
synchronized ,这是一个关键字,用来做线程锁,也是悲观锁 同步锁,当一个线程执行了被synchronized修饰的方法或代码块时,其他线程,将无法访问这个锁内的代码块,但是可以访问持有这个锁的线程的其他代码块。
非公平性:多个线程竞争一个synchronized 锁时,是随机分配给某个线程的,所以,synchronized 也是非公平锁。
可重入性:同一个线程,这个线程可以反复进入自己持有锁的代码块,这样做,每次进入后,持有的锁会+1,会记录持有锁的线程id。
static synchronized:锁住一个class类(普通synchronized锁住当前对象,即this)
偏向锁,轻量级锁,重量级锁:synchronized锁住一个代码块后,其他线程无法访问,这会导致性能低下,因此有了这三种锁来优化synchronized。
偏向锁:一个锁,一直被同一个线程访问,当这个线程已经获取过这个锁,再次想要获取这个锁时,不需要加锁就能访问锁内的代码块。
轻量级锁:出现一个新的线程,对偏向锁中的线程进行竞争一个锁,此时,新的线程会进入自旋,即持续等待,此时,就是轻量级锁。
重量级锁:轻量级锁的自旋一直存在,自旋一定次数后,升级为重量级锁,重量级锁就是最早的synchronized,会拒绝其他线程访问被锁住的代码块,这些线程将被挂起,进入阻塞。
2 CAS
这是一种乐观锁的实现:先查询需要修改的值,然后要进行修改时再次查询,如果两次结果一致,可以进行后面的修改这个值的操作,否则不断重复这个流程,进入自旋,直到可以修改,或者放弃修改。
具体实现类:AtomicInteger AtomicBoolean 等
3 ReenTrantLock
sync类重入获取资源:一种对同一个线程可重入锁,每次同一个线程重入,则state记录次数并+1,如果之前没有线程持有锁,则cas操作,将这个次数变为1。使用CAS来修改state的值,获取锁的sync类继承了AQS。
释放锁:对于同一个线程,每次将它的state -1,当state变成0的时候,将持有这个锁的线程清空,具体方法:setExclusiveOwnerThread(null)
非公平获取锁和资源:直接cas设置state,成功获取锁,失败进入队列等待。(非公平体现在等待队列之外的线程也会用同样的方式获取锁)
公平地获取锁和资源:state为0,要去cas给这个线程获取锁前,先判断这个线程是否在等待队列中。
4 ReentrantReadWriteLock
读写锁,分为读锁和写锁,读锁持有的时候不能再获取该资源的写锁,其他线程都能读,写锁持有的时候,其他线程不能读。
写锁获取锁:先查看资源是否被其他线程持有锁,再查看写锁的数量,可以则重入,state+1
具体情况:如果写锁数量不为0,查询持有锁线程是否为当前线程,是则重入,不是则 拒绝,可以重入还有判断是否超过写锁数量上限。
写锁释放:直接getstate-1,直到清除完,然后将持有资源的线程id置为null。
获取读锁:有写锁无法获取,只有读锁时,判断持有锁数量,不超过上限,state+1
读锁释放:使用CAS修改state 这个锁的数量。
5 AQS
一个抽象类,类中一些关键对象:
1 Node, 关键变量都用volatile修饰,事实上是一个双向链表,记录了上一个节点和下一个节 点的信息。exclusiveOwnerThread 记录当前持有锁的线程id,