5.1 Lock接口
Lock的意义在于提供了区别于synchronized的另一种具有更多广泛操作的同步方式,它能支持更多灵活的结构,并且可以关联多个Condition对象。
使用synchronized关键字将会隐式地获取锁,但是它将锁的获取和释放固化了,也就是先获取再释放。当然,这种方式简化了同步的管理,可是扩展性没有显示的锁获取和释放来的好。
5.2 AQS
学习编程两种思路:1,自顶向下:首先从距离用户最近的应用层,逐步往下探索;
2,自下而上:在了解了底层原理之后,再进行应用。
学习并发采用自下而上。
乐观锁的本质即是CAS,操作系统提供了支持CAS修改内存值的原子指令,所以乐观锁得以实现。从开发者的角度去看,虽然底层已经通过CAS实现了乐观锁,Java的底层已经在Unsafe这个类中封装了compareAndSwap方法,支持了对CAS原语的调用,为了使上层更加易用,需要经过进一步的抽象和封装。
5.2.1 队列同步器的接口与示例
AQS的设计是基于模板方法模式的,使用者需要继承同步器并重写指定的方法。
5.2.2 队列同步器的实现分析
1,属性
state用于判断共享资源是否正在被占用的标记位,volatile保证了线程之间的可见性。state的类型是int,为什么不是boolean? 用bolean来表示资源被占用与否语意上不是更明确吗?
这里就要谈到线程获取锁的两种模式,独占和共享。当一个线程以独占模式获取锁时,其他任何线程都必须等待,而当一个线程以共享模式获取锁时,其他也想以共享模式获取锁的线程也能够一起访问共享资源,但其他想以独占模式获取锁的线程需要等待。这就说明了,共享模式下,可能有多个线程正在共享资源,所以state需要表示线程占用数量,因此是int值。
队列头结点
队列尾结点
2,内部类
(1)ConditionObject
(2)Node:Node中主要存储了线程对象 (thread)、节点在队列里的等待状态 (waitStatus)、前后指针(prev、next)等信息。需要重点关注的是waitStatus这个属性,它是一个枚举值(如上图四个不同int值),AOS工作时必然伴随着Node的waitStatus值的变化,如果理解了waitStatus变化的时机,那对理解AOS整个工作原理有很大的帮助。
Node两种模式:独占和共享。
Node中方法predecessor()方法:
返回前置节点,如果为空则抛出空指针异常,前置任务(节点)不能为null时使用。
3,方法
两种使用场景:
1,尝试获取锁,不管有没有获取到,立即返回。
2,必须获取锁,如果当前时刻锁被占用,则进行等待。
(1)
tryAcquire是一个被 protected 修饰的方法,参数是一个int值,代表对int state的增加操作,返回值是boolean,代表是否成功获得锁。
该方法只有一行实现 throw new UnsupportedOperationException(),规定继承类必须override tryAcquire方法,否则就直接抛出UnsupportedOperationException。为什么这里一定需要上层自己实现? 因为尝试获取锁这个操作中可能包含某些业务自定义的逻辑,比如是否“可重入”等。
若上层调用tryAcquire返回true,线程获得锁,此时可以对相应的共享资源进行操作,使用完之后再进行释放。如果调用tryAcquire返回false,且上层逻辑上不想等待锁,那么可以自己进行相应的处理;如果上层逻辑选择等待锁,那么可以直接调用acquire方法,acquire方法内部封装了复杂的排队处理逻辑,非常易用。
(2)
acquire被final修饰,表示不允许子类擅自override,似乎是在表示: 等待并获取锁,我非常可靠,直接用就行,其他您就甭管了。
if判断条件包含了两部分:
!tryAcquire(arg)
acquireQueued(addwaiter(Node.EXCLUSIVE),arg)
如果tryAcquire获取锁成功,那么!tryAcquire为false,说明已经获取锁,不用再执行后续判断条件。根据判断条件的短路规则,直接返回。反之执行后续操作。
1,addWaiter方法:为当前线程和给定模式创建节点并排入队列。
2,acquireQueued方法:以独占模式不间断的模式,为队列中已存在的节点尝试获取锁,由条件等待方法和获取方法使用(?)
未完....
5.3 ReentrantLock
一句话对ReeentrantLock进行概括: ReentrantLock基于AQS,它实现了公平锁和非公平锁,在开发中可以用它对共享资源进行同步。此外,和synchronized一样,ReentrantLock支持可重入,但ReentrantLock在调度上更灵活,支持更多丰富的功能。
1,属性
2,内部类
(1)Sync ![](https://i-blog.csdnimg.cn/blog_migrate/e3b24e4a941a5d023143c09bd6495347.png)
方法
1,非final方法
,获取锁的操作,为空实现,需要子类根据自己的特征来实现。
用于反序列化。
2,final方法(重要)
(1)
逻辑:1,获取由AQS维护的state值
2,如果Sstate为0,则CAS的修改一次state,返回true;
3,如果state不为0,说明锁被占用,判断占用线程是否是当前线程(实现可重入性)
(2)
释放锁(完全或非完全释放)。
-(2)NonfairSync
方法
首先尝试CAS的获取锁并修改state,失败则调用AQS的acquire()。
两个问题:
1,可重入性
acquire时会首先调用tryAcquire尝试获得锁,而tryAcquire在NonfairSync中的实现是调用了nonfairTryAcquire(),满足可重入性。2
2,非公平性
(1)首先CAS的获取锁;
(2)在nonfairAcquire()中会尝试获得锁。
(3)FairSync
方法
直接调用AQS的acquire()方法
两个问题:
1,可重入性
调用acquire时,会先调用fairSync重写的tryAcquire()方法,其中定义了重入性代码。
2,公平性
1,在tryAcquire()中,首先判断是否锁是否空闲(c==0)
2,如果空闲,则调用AQS的判断队列中,除获得锁的节点外是否还有其他节点;
3,
3,方法
4,Java的中断机制
5.4 LockSupport工具
5.6 Condition接口
5.6.1 Condition接口与示例
Condition定义了等待(await)/通知(signal)两种类型的方法,当前线程调用这些方法时,需要提前获取到Condition对象关联的锁。Condition对象是由Lock对象(调用Lock对象的newCondition()方法)创建出来的,换句话说,Condition是依赖Lock对象的。
例1 例2
Condition中定义的方法如下:
注意:获取一个Condition必须通过Lock的newCondition()方法。
5.6.2 Condition的实现与分析
ConditionObject是同步器AbstractQueuedSynchronizer的内部类,因为Condition的操作需要获取相关联的锁,所以作为同步器的内部类也较为合理。每个Condition对象都包含着一个队 列(以下称为等待队列),该队列是Condition对象实现等待/通知功能的关键。
Condition的实现,主要包括:等待队列、等待和通知。
1,等待队列
2,等待
3,通知