J.U.C--locks--ReentrantLock

来源:互联网 发布:进程调度算法代码 编辑:程序博客网 时间:2024/05/19 04:29

在JDK5之前对共享变量的访问只有synchronized和volatile。在JDK5.0之后增加了显示锁ReentrantLock(可重入锁、独占锁)。需要注意的是,ReentrantLock并不是用来替代synchronized的,而是作为一种高级主题,在内置加锁机制synchronized不适用时作为一个可选的高级主题。

1. Lock接口

Lock是一个显示锁的接口,ReentrantLock就是Lock的一个实现类。Lock接口中定义了一组抽象的加锁操作。与内置的synchronized加锁操作不同,Lock提供:
1. 无条件的
2. 可轮询的;
3. 定时的;
4. 可中断的锁;
Lock的所有加锁和解锁都是显式的。Lock的实现类必须提供与内部锁相同的内存可见性原语。但是可以在一下方面不同:
1. 加锁语义;
2. 调度算法;
3. 顺序保证;
4. 性能特性。

下面贴出Lock接口的函数:

void lock();void lockInterruptibly() throws InterruptedException;boolean tryLock();boolean tryLock(long time, TimeUnit unit) throws InterruptedException;void unlock();Condition newCondition();

2. ReentrantLock

首先我们需要明确的一点是ReentrantLock的实现全部是基于同步器,也就是AQS这个类。ReentrantLock的所有核心方法都是通过AQS框架实现。

ReentrantLock实现了Lock接口并提供了与synchronized相同的互斥性和内存可见性。在获取ReentrantLock锁时有着与进入同步代码块相同的内存语义;在释放ReentrantLock锁时有着与退出同步代码块相同的内存语义;此外还提供了可重入的加锁语义。

下面给出一个Lock的标准使用形式:

Lock lock = new ReentrantLock();...lock.lock();try{    //更新对象状态    //捕获异常} finally{    lock.unlock();}

切记,必须在finally块中释放掉Lock,否则,如果在try块中抛出了异常,这个锁就永远不能被释放了。

1. ReentrantLock的属性

ReentrantLock类里面只有一个属性,也就是一个Sync变量,Sync这是一个内部类,继承于AbstractQueuedSynchronizer。

/** 同步器提供了所有的实现机制 */private final Sync sync;

2. ReentrantLock的类继承图,

这里写图片描述

3. ReentrantLock的构造器

ReentrantLock有两个构造器,无参和带参的构造器;

public ReentrantLock() {    sync = new NonfairSync();}public ReentrantLock(boolean fair) {    sync = fair ? new FairSync() : new NonfairSync();}

无参的构造器默认的是new一个非公平的锁。带参的构造器可以可以通过指定布尔类型的参数确定new公平的或则是不公平的锁。
这里的公平是指:
1. 公平的锁: 线程将按照它们发出的请求顺序来获得锁;
2. 非公平的锁: 允许”插队”,当一个线程请求非公平的锁时,如果在发出请求的同时,该锁的状态变成了可用,那么该线程将跳过队列中所有等待线程并获得锁。这样就避免了线程的切换。

4. 内部类

上面说了ReentrantLock是基于AQS实现的,在ReentrantLock内部中也只定义了一个属性:Sync的一个实例。其实Sync就是ReentrantLock内部继承于AQS的类。Sync又有两个实现子类:公平锁FairSync和非公平锁NonfairSync。其继承管理如下图:
这里写图片描述

从构造器我们也可以知道,ReentrantLock实际上默认使用的是NonfairSync,通过指定也可以使用FairSync。

在这两个类中都只有两个重载的方法:
lock()方法和tryAcquire(int acquires)方法。如下图:
这里写图片描述

5. 重点方法:

这里只分析加锁和释放锁两个方法:

lock()unlock()

1)ReentrantLock.lock()方法

在ReentrantLock类中的lock()方法很简单,直接调用了同步器Sync的lock()方法,源码如下:

public void lock() {    sync.lock();}

我们直接看Sync中的lock()方法,通过构造器我们可知,我们使用的同步器默认是NonfairSync,也可以通过指定也可以使用FairSync。我们就把这两个类中的 lock()函数源码给出做个对比:

//NonfairSync类中final void lock() {    //首先让当前线程获取锁    if (compareAndSetState(0, 1))        setExclusiveOwnerThread(Thread.currentThread());    else//当前线程获取锁失败之后再从同步队列中取出线程来获取锁        acquire(1);}//FairSync类中final void lock() {    acquire(1);}

逻辑分析:
(1)NonfairSync非公平锁中,由源码可知他不是按照请求顺序来给线程分配锁的,而是允许”插队”,当一个线程请求非公平的锁时,如果在发出请求的同时,该锁的状态变成了可用,那么该线程将跳过队列中所有等待线程并获得锁,这样就避免了线程的切换。

对于当前线程获取锁:如果锁可用,通过维护AQS中的state属性,通过CAS设置state值由0到1。否则调用acquire(1)函数,这个函数的实现具体可看AQS源码解析那一节:http://blog.csdn.net/u010853261/article/details/54747421

(2)FairSync公平锁中,直接从同步队列中按照请求顺序来获取锁。

2)ReentrantLock.unlock()方法

对于unlock()方法,在NonfairSync和FairSync中没有什么区别。都是调用AQS中的release()方法。

public void unlock() {    sync.release(1);}

release()在AQS这篇博文中也有讲。传送门:
http://blog.csdn.net/u010853261/article/details/54747421

0 0