3.java并发机制及实现原理(2)

2021-02-19  本文已影响0人  农民工进城

说到java并发编程,就不得不说synchronized关键字,很多人称之为“重量级锁”,其实JAVASE 1.6对ynchronized进行的各种优化,synchronized并不会显得那么重了

本章要点

1.锁升级
2.synchronized
3.Lock

  1. AbstractQueuedSynchronizer

java中任何一个对象都可以作为锁

3.1锁升级

锁级别分为:无锁-->偏向锁-->轻量级锁-->重量级锁(真正意义上的加锁)

偏向锁:


偏向锁.png

轻量级锁:


轻量级锁.png

3.2 synchronized 基本原理

synchronized是基于ObjectMonitor实现的,ObjectMonitor中有几个关键属性:
_owner:指向持有ObjectMonitor对象的线程
_WaitSet:存放处于wait状态的线程队列
_EntryList:存放处于等待锁block状态的线程队列
_recursions:锁的重入次数
_count:用来记录该线程获取锁的次数
1.只有_EntryList线列才有权利争抢锁资源 2.调研notify或者notifyAll方法,其实就是将线程从_WaitSet移到_EntryList中,然后让其去争抢锁资源
总之,被synchronized修饰的代码,在被编译器编译后在被修饰的代码前后加上了一组字节指令。代码开始位置加上monitorenter,在代码结束位置加入monitorexit,这两个字节码指令配合完成了synchronized关键字修饰代码的互斥访问。
当执行monitorenter时,若对象未被锁定时,或者当前线程已经拥有了此对象的monitor锁,则锁计数器+1,该线程获取该对象锁。
当执行monitorexit时,锁计数器-1,当计数器为0时,此对象锁就被释放了。那么其他阻塞的线程则可以请求获取该monitor锁。

3.3 Lock

Lock接口中包含的方法:

ReadWriteLock锁

可重入锁:如果锁具备可重入性,当某个线程成功获取锁,调用了某个同步方法method1之后,此时如果再去调用同步方法method2,就不需要再去获取锁,而只是增加获取次数,直接去执行。

ReadWriteLock与synchronized的区别:

(1)synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。
ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。
(2)synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;
ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。
(3)synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以相应中断。
(4)synchronized是非公平锁, ReentrantLock可以实现公平锁

ReentrantReadWriteLock 读写锁

读写锁:适用于读多写少的场景。
ReentrantReadWriteLock,有两把锁,即:一个是读操作相关的锁(ReadLock),称为共享锁;一个是写相关的锁(WriteLock),称为排他锁

3.4 AbstractQueuedSynchronizer(简称AQS)

AQS使用一个FIFO的队列表示排队等待锁的线程,队列头节点称作“哨兵节点”,它不与任何线程关联。其他的节点与等待线程关联,每个节点维护一个等待状态waitStatus。


image.png

ReentrantLock的基本实现可以概括为:先通过CAS(预期值:0,设置为:1)尝试获取锁,如果CAS操作成功,则将当前线程设置为exclusiveOwnerThread线程;如果CAS操作失败或已经有线程占据了锁,那就加入AQS队列并且被挂起。当锁被释放之后,排在CLH队列队首的线程会被唤醒,然后CAS再次尝试获取锁。在这个时候,如果:

非公平锁:如果同时还有另一个线程进来尝试获取,那么有可能会让这个线程抢先获取;

公平锁:如果同时还有另一个线程进来尝试获取,当它发现自己不是在队首的话,就会排到队尾,由队首的线程获取到锁

上一篇下一篇

猜你喜欢

热点阅读