1. synchronize锁升级:无锁,偏向锁,轻量锁,重量锁 (看病:社区医院->三甲医院)
1.1 概述
按照获得锁和释放锁的性能消耗,锁的分类:
1. 无锁状态
2. 偏向锁:不进行CAS, 测试对象头Mark Word里是否存储当前线程的偏向锁,如果有则获得锁。 竞争出现释放锁。
3. 轻量级锁:CAS
4. 重量级锁
synchronized锁: 由对象头中的Mark Word根据锁标志位的不同而被复用及锁升级策略。
锁升级功能主要依赖MarkWord中锁标志位和释放偏向锁标志位。
为什么引入锁升级:尽量减少用户态和内核态的切换,因为他们容易导致阻塞。
1.2 无锁:程序不会有锁的竞争
1.3 偏向锁:单线程竞争,指向线程号(java15以后废除,cost过大)
介绍:
主要作用:当一段同步代码一直被同一个线程多次访问,由于只有一个线程那么该线程在后续访问时便会自动获得锁(偏向锁)
同一个老顾客来访,直接老规矩行方便
偏向锁为了解决只有在一个线程执行同步时提高性能
如果不存在其他线程竞争,那么持有偏向锁的线程将永远不需要同步。
偏向锁的撤销:偏向锁使用一种等到竞争出现才释放锁的机制,只有当其他线程竞争锁时,持有偏向锁的原来线程才会被撤销。撤销需要等待全局安全点(该时间点上没有字节码正在执行),同时检查持有偏向锁的线程是否还在执行
理论:
过程:
1.4 轻量锁:多线程竞争,但任意时刻只有一个线程竞争,不存在锁线程竞争太过激烈的情况,也就没有线程阻塞
主要作用:
本质就是自旋锁
有线程来参与锁的竞争,但是获取锁的冲突时间极短
过程:
1.5 重量锁:会有用户态、内核态切换。指向互斥量,monitor指针
1.6 总结
1.6.1 分类
1.6.2 锁升级流程:先自旋,不行再阻塞
2. 无锁,独占锁/排他锁,读写锁,邮戳锁 (ReentrantLock ReentrantReadWriteLock StampedLock)
java.util.concurrent.locks
2.0 Lock
2.1 演变
1. 排他锁:同一时刻只允许一个线程访问。 如:Mutex, Reentrantlock, synchronized
2. 读写锁:读写分离。同一时刻允许多个读锁和一个写锁。
2.2 独占锁/排他锁
2.3 ReentrantReadWriteLock 可重入读写锁
重点介绍 ReentrantReadWriteLock 可重入读写锁,继承 ReentrantLock,实现ReadWriterLock接口
读写锁定义:一个资源能够被多个读线程访问,或者被一个写线程访问,但不能同时存在读写线程
一句话:只允许读读互存,其他互斥
ReentrantReadWriteLock锁降级:为了保证数据一致性
2.4 StampedLock 版本锁,票据锁:比读写锁更快
它是由锁饥饿问题引出:
ReentrantReadWriteLock实现了读写分离,但是一旦读操作比较多的时候,想要获取写锁就变得比较困难了,假如当前1000个线程,999个读,1个写,有可能999个读取线程长时间抢到了锁,那1个写线程就悲剧了 因为当前有可能会一直存在读锁,而无法获得写锁,根本没机会写,
如何缓解锁饥饿问题:
使用“公平”策略可以一定程度上缓解这个问题
但是“公平”策略是以牺牲系统吞吐量为代价的
因此,StampedLock类的乐观读锁闪亮登场
StampedLock的特点:
- 所有获取锁的方法,都返回一个邮戳(Stamp),Stamp为零表示获取失败,其余都表示成功;
- 所有释放锁的方法,都需要一个邮戳(Stamp),这个Stamp必须是和成功获取锁时得到的Stamp一致;
- StampedLock是不可重入的,危险(如果一个线程已经持有了写锁,再去获取写锁的话就会造成死锁)
StampedLock有三种访问模式:
- Reading(读模式):功能和ReentrantReadWriteLock的读锁类似
- Writing(写模式):功能和ReentrantReadWriteLock的写锁类似
- Optimistic reading(乐观读模式):无锁机制,类似于数据库中的乐观锁,支持读写并发,很乐观认为读取时没人修改,假如被修改再实现升级为悲观读模式
StampedLock的缺点
- StampedLock 不支持重入,没有Re开头
- StampedLock 的悲观读锁和写锁都不支持条件变量(Condition),这个也需要注意。
- 使用 StampedLock一定不要调用中断操作,即不要调用interrupt() 方法
- 如果需要支持中断功能,一定使用可中断的悲观读锁 readLockInterruptibly()和写锁writeLockInterruptibly()
3. Locks锁包下的辅助类
3.1 CountDownLatch
3.2 CyclicBarrier
3.3 Semaphore
类比:抢车位
1. CountDownLatch:允许一个或多个线程等待其他线程完成操作。
2. CyclicBarrier:字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一
组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会
开门,所有被屏障拦截的线程才会继续运行。
CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重
置。所以CyclicBarrier能处理更为复杂的业务场景。例如,如果计算发生错误,可以重置计数
器,并让线程重新执行一次。
CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得Cyclic-Barrier
阻塞的线程数量。isBroken()方法用来了解阻塞的线程是否被中断。
3. 控制并发线程数的Semaphore: Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。
4. 线程间交换数据的Exchanger: Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。
标签:ReentrantReadWriteLock,读写,StampedLock,读锁,线程,JUC5,多线程,偏向 From: https://blog.51cto.com/u_15905340/5919910