J.U.C--locks--ReentrantLock

在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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值