java并发编程之美——高级篇(三)

一、抽象同步队列AQS概述

1.什么是AQS?

AbstractQueuedSynchronizier抽象同步队列。它是实现同步器的基本组件。并发包中锁的底层就是使用AQS实现的。首先,AbstractQueuedSynchronizier类继承AbstractOwnableSynchronizer类。包含Node类型和ConditionObject类型的成员变量。AQS是一个FIFO的双向队列,内部通过节点head和tail记录队首和队尾的元素,队列元素类型为Node。

AQS的类图结构:AQS实现AbastractOwnableSynchronizer,包含内部类Node和ConditionObject

Node中的thread用来存放进入AQS里面的线程,Node节点内部的SHARED用来标记该线程是获取共享资源时被阻塞挂起后放入AQS队列的线程,EXCLUSIVE用来标记获取独占资源时被挂起后放入AQS队列的,waitStatus记录当前线程的等待状态,可以为CANCELLED(线程被取消了)、SIGNAL(线程需要被唤醒)、CONDITION(线程在条件队列里面等待)、PROPAGATE(释放共享资源时需要通知其他节点);prev记录当前节点的前驱节点,next记录当前节点的后继节点。

AQS中的变量state。、

AQS中的内部类ConditionObject。用来结合锁实现线程同步。ConditionObject可以访问AQS对象内部的变量,比如state和AQS队列。ConditionObject是条件变量,每个条件变量对应一个条件队列,用来存放调用条件变量的await方法后被阻塞的线程。

对于AQS来说,线程同步的关键在于对state的修改,根据state是否属于一个线程,操作state的方式分为独占方式和共享方式。

获取与释放锁资源需要实现类重写获取与释放的方法,根据不同的实现也就产生了独占锁和共享锁。重写isHeldExclusively方法,判断是独占锁还是共享锁。

2.AQS条件变量 ConditionObject

条件变量的await,notify用来配合锁实现线程同步。

二、独占锁ReentrantLock的原理

ReentrantLock是可重入的独占锁。也是通过AQS实现。

主要方法:

(1)void lock()

获取锁,根据ReentrantLock的构造参数来获取一个公平锁或非公平锁。

(2)void lockInteruptibly

获取锁,不同在于,该方法会对中断进行相应,当前线程在调用该方法是,如果其他线程调用了当前线程的interrupt()方法,该方法会抛出InterruptedException异常。

(3)boolean tryLock()

尝试获取锁,如果当前锁没有被其他线程占用,则当前线程获取该锁并返回true,否则返回false。该方法不会引起当前线程阻塞。

(4)boolean tryLock(long timeout,TimeUnit unit)

超时未获取到锁返回false

(5)void unlock()

state减1,更新可重入次数。如果为0则释放锁。

三、ReentranReadWriteLock的原理

ReentranReadWriteLock采用读写分离的策略,允许多个线程同时获取读锁。

类图结构:

 实现了ReadWriteLock接口。内部维护一个ReadLock和一个WriteLock。它们依赖Sync实现具体功能。而Sync继承了AQS,并且也提供了公平锁和非公平锁的实现。AQS中维护一个state状态,而ReentranReadWriteLock用state的高16位表示读状态,也就是读锁的次数,低16位表示写锁的线程的可重入次数。

四、JDK新增的StampLock

JDK8新增的一个锁,提供了三种模式的读写控制。获取锁时会返回一个long类型的stamp,失败会返回0。当调用释放锁和转换锁的方法时,需要传入该参数。

------------------------------------------------未完待续-------------------------------------------------

猜你喜欢

转载自www.cnblogs.com/feifei123/p/12682586.html