1.锁的一路演变

2.ReentrantReadWriteLock锁降级

3.比读写锁更快的锁————邮戳锁

4.总结

1.锁的一路演变
当咱们在学习java的锁的时候,经验了以下四个阶段的锁演变:无锁→独占锁→读写锁→邮戳锁

无锁:
咱们一开始学会编写代码的时候,必定写的都是无锁的代码。
长处:执行效率高
毛病:多线程无序争夺导致谬误数据

而后咱们发现其中的问题,就学会了synchronized, reentrantlock
长处:串行化保障了数据一致性
毛病:所有操作互斥,执行效率低

接着咱们发现这样子效率太低,如果读线程占大多数,写线程占多数,就又去学习了ReentrantReadWriteLock
长处:读读共享,读写互斥,晋升了大面积的共享性能
毛病:读线程还没完结永,写线程永远不可能会取得锁(造成锁饥饿)

而后其实还有比读写锁更快的锁StampedLock(咱们会在下文进行解说)
长处:读的过程中也容许获取写锁染指,效率更高
毛病:不反对重入,不反对Condition,也不反对中断

这样就有了以下这样的表格:

无锁synchronized, reentrantlockReentrantReadWriteLockStampedLock
长处执行效率高串行化保障了数据一致性读读共享,读写互斥,晋升了大面积的共享性能读的过程中也容许获取写锁染指,效率更高
毛病多线程无序争夺导致谬误数据所有操作互斥,执行效率低读线程还没完结永,写线程永远不可能会取得锁(造成锁饥饿)不反对重入,不反对Condition,也不反对中断

2.ReentrantReadWriteLock锁降级

咱们后面刚学习了JAVA并发编程——Synchronized与锁降级,明天咱们来学习一下锁降级。

咱们先来看一下ReentrantReadWriteLock锁的定义:
一个资源能别多个读线程拜访,或者被一个写线程拜访。
也就是说:
读读不互斥
写写互斥
读写互斥

只有在读多写少情境之下,读写锁才具备较高的性能体现。

而锁降级又是什么呢?
锁降级
将写入锁降级为读锁(就像linux文件读写权限,写权限肯定会高于读权限)
换句话说,我们在lock.writeLock();的同时,能够再进行lock.readLock(),这个时候读锁就会降级成写锁,反之则不行,程序会死锁

这样说咱们可能还是看不太懂,咱们间接用代码解释好了。

import java.util.concurrent.locks.ReentrantReadWriteLock;/** * 锁降级:遵循获取写锁→再获取读锁→再开释写锁的秩序,写锁可能降级成为读锁。* * 如果一个线程占有了写锁,在不开释写锁的状况下,它还能占有读锁,即写锁降级为读锁。*/public class LockDownGradingDemo{public static void main(String[] args)    {        ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();        ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();        writeLock.lock();        System.out.println("-------正在写入");        readLock.lock();        System.out.println("-------正在读取");        writeLock.unlock();    }}

读写锁降级的目标:
在高并发的状况下,咱们为了让程序感知到咱们批改了内容,就先用读锁锁住这个后果,不让其它写线程进来,因为读读是能够共享的,保障了该次变动的数据可见性。

以下摘自ReentrantReadWriteLock的源码:

1 代码中申明了一个volatile类型的cacheValid变量,保障其可见性。

2 首先获取读锁,如果cache不可用,则开释读锁,获取写锁,在更改数据之前,再查看一次cacheValid的值,而后批改数据,将cacheValid置为true,而后在开释写锁前获取读锁;此时,cache中数据可用,解决cache中数据,最初开释读锁。这个过程就是一个残缺的锁降级的过程,目标是保证数据可见性。

如果违反锁降级的步骤
如果以后的线程C在批改完cache中的数据后,没有获取读锁而是间接开释了写锁,那么假如此时另一个线程D获取了写锁并批改了数据,那么C线程无奈感知到数据已被批改,则数据呈现谬误。
如果遵循锁降级的步骤
线程C在开释写锁之前获取读锁,那么线程D在获取写锁时将被阻塞,直到线程C实现数据处理过程,开释读锁。这样能够保障返回的数据是这次更新的数据,该机制是专门为了缓存设计的。

3.比读写锁更快的锁————邮戳锁

因为读写锁有锁饥饿的问题。
锁饥饿:如果当初有1000个线程,999个读,1个写,那就是读线程长时间占据锁,而写线程长时间无奈获取锁。

那么如何缓解锁饥饿问题?咱们有上面这几个解决办法
1)应用偏心锁策略能够肯定水平上缓解这个问题,然而吞吐量不高
2)应用邮戳锁

为了解决这个问题,咱们应用邮戳锁:
SteampedLock有三种拜访模式
1)Reading(读模式):性能和ReentrantReadWriteLock读锁相似
2)Writing(写模式):性能和ReentrantReadWriteLock写锁相似
3)Optimistic reading(乐观读模式):无锁机制,相似于数据库中的乐观锁,反对读写并发,很乐观认为读取时没人批改,如果被批改再实现为乐观读模式。

    //乐观读    //咱们通过刚开始取得的版本号开判断是不是有人动过这个数据    //而后如果有人批改过,再进行锁降级    public void tryOptimisticRead() {        //先获取一个乐观读标记位        long stamp = stampedLock.tryOptimisticRead();        int result = number;        //距离4秒钟,咱们很乐观的认为没有其余线程批改过number值,理论靠判断。        System.out.println("4秒前stampedLock.validate值(true无批改,false有批改)" + "\t" + stampedLock.validate(stamp));        for (int i = 1; i < 4; i++) {            try {                TimeUnit.SECONDS.sleep(1);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println(Thread.currentThread().getName() + "\t 正在读取中......" + i +                    "秒后stampedLock.validate值(true无批改,false有批改)" + "\t"                    + stampedLock.validate(stamp));        }        if (!stampedLock.validate(stamp)) {            System.out.println("有人动过--------存在写操作!");            stamp = stampedLock.readLock();            try {                System.out.println("从乐观读 降级为 乐观读");                result = number;                System.out.println("从新乐观读锁通过获取到的成员变量值result:" + result);            } catch (Exception e) {                e.printStackTrace();            } finally {                stampedLock.unlockRead(stamp);            }        }        System.out.println(Thread.currentThread().getName() + "\t finally value: " + result);    }

4.总结
这次咱们学习的锁的演变,每一种锁都有各自的优缺点,再上一下下面那个表格。

无锁synchronized, reentrantlockReentrantReadWriteLockStampedLock
长处执行效率高串行化保障了数据一致性读读共享,读写互斥,晋升了大面积的共享性能读的过程中也容许获取写锁染指,效率更高
毛病多线程无序争夺导致谬误数据所有操作互斥,执行效率低读线程还没完结永,写线程永远不可能会取得锁(造成锁饥饿)不反对重入,不反对Condition,也不反对中断