Jdk1.6 JUC源码解析(7)-locks-ReentrantLock
- Java代码层面提供的锁机制,可做为Synchronized(jvm内置)的替代物,和Synchronized一样都是可重入的。
- 与Synchronized相比较而言,ReentrantLock有以下优 势:支持公平/非公平锁、支持可中断的锁、支持非阻塞的tryLock(可超时)、支持锁条件、可跨代码块使用(一个地方加锁,另一个地方解锁),总之比 Synchronized更加灵活。但也有缺点,比如锁需要显示解锁、无法充分享用JVM内部性能提升带来的好处等等。
- ReentrantLock实现了Lock接口,先来看下这个接口:
public interface Lock {
/**
* 获取锁,如果锁无法获取,当前线程被阻塞,直到锁可以获取并获取成功为止。
*/
void lock();
/**
* 在当前线程没有被中断的情况下获取锁。
* 如果获取成功,方法结束。
* 如果锁无法获取,当前线程被阻塞,直到下面情况发生:
* 1.当前线程(被唤醒后)成功获取锁。
* 2.当前线程被其他线程中断。
*/
void lockInterruptibly() throws InterruptedException;
/**
* 如果当前锁是可用的,获取锁。
* 获取成功后,返回true。
* 如果当前锁不可用,返回false。
*/
boolean tryLock();
/**
* 如果锁在给定超时时间内可用,并且当前线程没有被中断,那么获取锁。
* 如果锁可用,获取锁成功并返回true。
* 如果锁无法获取,当前线程被阻塞,直到下面情况发生:
* 1.当前线程(被唤醒后)成功获取锁。
* 2.当前线程被其他线程中断。
* 3.指定的等待时间超时。
*/
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
/**
* 释放锁。
*/
void unlock();
/**
* 返回一个和当前锁实例相关联的条件。
* 当前线程必须首先获取锁后才能在锁条件上等待。
* 一个Condition的await()方法调用会在等待之前自动释放锁,在等待结束
* 前重新获取锁。
*/
Condition newCondition();
}
- 之前分析AQS的时候提到过,基于AQS构建的同步机制都会使用内部帮助类继承AQS的方式构建,看下ReentrantLock中的同步机制:
//内部同步机制的引用。
private final Sync sync;
/**
* 这个锁实现的基本同步控制机制,下面会提供公平和非公平版本的子类。
* 利用AQS的state来表示锁持有(重入)的次数。.
*/
static abstract class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
/**
* 方法用来支持非公平的tryLock
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//如果当前没有任何线程获取锁(锁可用),尝试设置state。
if (compareAndSetState(0, acquires)) {
//如果设置成功,将当前线程信息设置到AQS中(所有权关联)。
setExclusiveOwnerThread(current);
return true;
}
}
//如果锁已经被持有,那么判断一下持有锁的线程是否为当前线程。
else if (current == getExclusiveOwnerThread()) {
//如果是当前线程在持有锁,那么这里累计一下重入次数。
int nextc = c + acquires;
if (nextc < 0) // overflow 重入次数最大不能超过int的最大值
throw new Error("Maximum lock count exceeded");
//设置到AQS的state中
setState(nextc);
return true;
}
//如果锁已经被持有,且持有线程不是当前线程,返回false。
return false;
}
protected final boolean tryRelease(int releases) {
//释放时,这里要减去重入次数。
int c = getState() - releases;
//判断控制权关系是否正确。
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
//如果当前线程完全释放了锁(重入次数为0)
free = true;
//解除所有权关系。
setExclusiveOwnerThread(null);
}
//设置重入次数。
setState(c);
//返回是否释放成功(或者说是否完全释放)。
return free;
}
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes this lock instance from a stream.
* @param s the stream
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
接下来先看一下非公平版本的子类:
/**
* Sync object for non-fair locks
*/
final static class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
//这里首先尝试一个短代码路径,直接CAS设置state,尝试获取锁。
//相当于一个插队的动作(可能出现AQS等待队列里有线程在等待,但当前线程竞争成功)。
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);//如果CAS失败,调用AQS的独占请求方法。
}
protected final boolean tryAcquire(int acquires) {
//调用上面父类的nonfairTryAcquire方法。
return nonfairTryAcquire(acquires);
}
}
再来先看一下公平版本的子类:
/**
* Sync object for fair locks
*/
final static class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* 公平版本的tryAcquire。
* 只有在递归(重入)或者同步队列中没有其他线程
* 或者当前线程是等待队列中的第一个线程时才准许访问。
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) { //如果当前锁可用,且同步等待队列中没有其他线程,那么尝试设置state
setExclusiveOwnerThread(current); //如果设置成功,相当于获取锁成功,设置所有权关系。
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
//如果当前线程已经持有该锁,那么累计重入次数。
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
- 当前线程首先会无条件的执行一个CAS操作来获取锁,如果CAS操作成功,获取锁成功。
- 如果第1步没成功,当前会检查锁是否被其他线程持有,也就是锁是否可用。
- 如果没有其他线程持有锁,会以CAS的方式尝试获取锁,如果CAS操作成功,获取锁成功。
- 如果有其他线程持有锁,会判断一下持有锁的线程是否为当前线程,如果是当前线程,重入次数+1,获取锁成功。
- 根据AQS的分析,上述2、3、4步会执行多次,如果最终获取锁失败,当前线程会被阻塞,等待其他线程执行解锁操作将其唤醒。
- 当前线程首先会检查锁是否被其他线程持有,并且当前同步等待队列里有没有其他线程在等待。
- 如果没有其他线程持有锁,且同步等待队列里没有其他线程,会以CAS的方式尝试获取锁,如果CAS操作成功,获取锁成功。
- 如果有其他线程持有锁,会判断一下持有锁的线程是否为当前线程,如果是当前线程,重入次数+1,获取锁成功。
- 根据AQS的分析,上述1、2、3步会执行多次,如果最终获取锁失败,当前线程会被阻塞,等待其他线程执行解锁操作将其唤醒。
- 当前线程首先将锁重入次数减1(AQS的state),如果减1后结果为0,将当前同步器的线程信息置空,并唤醒同步等待队列中队头的等待线程。
- 如果第1步中,重入次数减1后结果不为0(说明当前线程还持有当前锁),方法结束。
- 有了内部的基础同步机制,ReentrantLock的实现就很简单了,直接看代码:
/**
* 默认情况下构建非公平锁。
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* 根据给定的公平策略生成相应的实例。
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = (fair)? new FairSync() : new NonfairSync();
} public void lock() {
sync.lock();
} public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
} public boolean tryLock() {
return sync.nonfairTryAcquire(1);
} public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
} public void unlock() {
sync.release(1);
} public Condition newCondition() {
return sync.newCondition();
}
最后看一下一些支持监测的方法:
/**
* 获取当前线程的对当前锁的持有(重入)次数。
*/
public int getHoldCount() {
return sync.getHoldCount();
}
/**
* 判断当前锁是否被当前线程持有。
*/
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
/**
* 判断当前锁是否被(某个线程)持有。
*/
public boolean isLocked() {
return sync.isLocked();
}
/**
* 当前锁是否为公平锁。
*/
public final boolean isFair() {
return sync instanceof FairSync;
}
/**
* 获取持有当前锁的线程。
*/
protected Thread getOwner() {
return sync.getOwner();
}
/**
* 判断是否有线程在当前锁的同步等待队列中等待。
*/
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
/**
* 判断给定的线程是否在当前锁的同步等待队列中等待。
*/
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
/**
* 获取当前锁的同步等待队列中的等待线程(估计)数量。
*/
public final int getQueueLength() {
return sync.getQueueLength();
}
/**
* 获取当前锁的同步等待队列中等待的线程。
*/
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
/**
* 判断是否有线程在给定条件的条件等待队列上等待。
*/
public boolean hasWaiters(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}
/**
* 获取给定条件的条件等待队列中等待线程的(估计)数量。
*/
public int getWaitQueueLength(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
}
/**
* 获取给定条件的条件等待队列中等待线程。
*/
protected Collection<Thread> getWaitingThreads(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
}
Jdk1.6 JUC源码解析(7)-locks-ReentrantLock的更多相关文章
- Jdk1.6 JUC源码解析(12)-ArrayBlockingQueue
功能简介: ArrayBlockingQueue是一种基于数组实现的有界的阻塞队列.队列中的元素遵循先入先出(FIFO)的规则.新元素插入到队列的尾部,从队列头部取出元素. 和普通队列有所不同,该队列 ...
- Jdk1.6 JUC源码解析(6)-locks-AbstractQueuedSynchronizer
功能简介: AbstractQueuedSynchronizer(以下简称AQS)是Java并发包提供的一个同步基础机制,是并发包中实现Lock和其他同步机制(如:Semaphore.CountDow ...
- Jdk1.6 JUC源码解析(1)-atomic-AtomicXXX
转自:http://brokendreams.iteye.com/blog/2250109 功能简介: 原子量和普通变量相比,主要体现在读写的线程安全上.对原子量的是原子的(比如多线程下的共享变量i+ ...
- Jdk1.6 JUC源码解析(13)-LinkedBlockingQueue
功能简介: LinkedBlockingQueue是一种基于单向链表实现的有界的(可选的,不指定默认int最大值)阻塞队列.队列中的元素遵循先入先出 (FIFO)的规则.新元素插入到队列的尾部,从队列 ...
- 【JUC源码解析】ScheduledThreadPoolExecutor
简介 它是一个线程池执行器(ThreadPoolExecutor),在给定的延迟(delay)后执行.在多线程或者对灵活性有要求的环境下,要优于java.util.Timer. 提交的任务在执行之前支 ...
- 【JUC源码解析】SynchronousQueue
简介 SynchronousQueue是一种特殊的阻塞队列,该队列没有容量. [存数据线程]到达队列后,若发现没有[取数据线程]在此等待,则[存数据线程]便入队等待,直到有[取数据线程]来取数据,并释 ...
- 【JUC源码解析】ForkJoinPool
简介 ForkJoin 框架,另一种风格的线程池(相比于ThreadPoolExecutor),采用分治算法,工作密取策略,极大地提高了并行性.对于那种大任务分割小任务的场景(分治)尤其有用. 框架图 ...
- 【JUC源码解析】DelayQueue
简介 基于优先级队列,以过期时间作为排序的基准,剩余时间最少的元素排在队首.只有过期的元素才能出队,在此之前,线程等待. 源码解析 属性 private final transient Reentra ...
- 【JUC源码解析】CyclicBarrier
简介 CyclicBarrier,一个同步器,允许多个线程相互等待,直到达到一个公共屏障点. 概述 CyclicBarrier支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后 ...
随机推荐
- PRINCE2学习
今天对PRINCE2中提及的7大主题进行学习,主要的内容是通过概述和PMBOK之间的对比做一些总结,每个主题所包含的过程和方法并没有太多涉及,没有对整个体系有全面深入的学习,有些断章取义的地方也请博友 ...
- STM32驱动OV7725摄像头颜色识别
实验目的: 使用stm32驱动OV7725摄像头进行图像实时采集,在tft屏幕上实时显示并识别图像中的特定颜色,在颜色的周围画上框. 实验现象: 我的工程代码链接: http://download.c ...
- 【CreateJS】WebStorm+Adobe Animate CC 搭配开发HTML5,入门教程
目的:动画设计师用Adobe Animate CC做好动画素材,发布好之后,交给程序员写交互代码:在WebStorm之类的ide里操纵 Animate 里面的变量,class等. 前提环境: ①安装好 ...
- poptest老李谈数据库优化总结
poptest老李谈数据库优化总结 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:9088 ...
- 以正确的姿势实现一棵JavaScript菜单树
菜单树是常见的前端特效, 一般长下面这样 还有各种形态的变种, 有长这样的 也有长这样的 尽管这些菜单的相貌都不尽相同, 在功能实现的本质上却都是相同的.实现程序的大致流程如下 读取服务器端的菜单数据 ...
- Nginx VS Apache
作为 Web 服务器:相比 Apache,Nginx 使用更少的资源,支持更多的并发连接,体现更高的效率 Nginx 静态处理性能比 Apache 高 3倍以上 最核心的区别在于apache是同步多进 ...
- 深度学习的2016: NIPS 2016速览
With best wishes for a happy New Year! NIPS(Nerual Information Processing Systems)是人工智能.机器学习领域的顶级学术会 ...
- Java中ArrayList,Vector,LinkedList,HashMap,HashTable,HashSet对比及总结
1.所有的集合的父类都是Collection的接口 2.Set List Map 区别 A 在Set里面:无法添加元素的顺序,所以Set里面的元素不能重复 B 在List中:有索引号,类似于数组, ...
- 一个只有99行代码的JS流程框架
张镇圳,腾讯Web前端高级工程师,对内部系统前端建设有多年经验,喜欢钻研捣鼓各种前端组件和框架. 最近一直在想一个问题,如何能让js代码写起来更语义化和更具有可读性. 上周末的时候突发奇想,当代码在运 ...
- linux环境下安装jdk(本文示例是jdk1.6.0_export JAVA_HOME=/usr/java/jdk1.6.0_45 export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar45)
第一步:创建一个文件夹安装jdk(虽说地址一般自定义,但是为了方便查找请按照笔者建议目录 ):/usr/java 将jdk-6u45-linux-x64.bin文件放到 /usr/java 文件夹 ...