ReentrantLock实现了Lock接口。ReentrantLock是可重入锁,支持同一个线程对资源的重复加锁。

简单用法示例:

    public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
System.out.println("执行业务操作");
} finally {
lock.unlock();
}
}

ReentrantLock有3个静态内部类:Sync、NonfairSync、FairSync,其中Sync是AQS的子类,是抽象类,有一个抽象的void lock()方法。NonfairSync和FairSync都是Sync的子类,前者是非公平实现,后者是公平实现,这两个类均重写了Sync的lock()方法和AQS的tryAcquire(int acquires)方法。

ReentrantLock的lock()方法内部就是调用Sync实例的lock()方法:

假如是默认的非公平实现的话,则会调用NonfairSync的lock()方法,内部实现是先CAS设置state,由0设置成1,如果设置成功,则把当前线程设置为获取独占锁的线程,如果设置失败,则调用acquire(1)。acquire方法是AQS的方法,内部会先调用tryAcquire()方法,tryAcquire也是AQS的方法,但是AQS没有实现,需要具体实现类实现,这里由NonfairSync具体实现。如果tryAcquire方法返回true,则表示线程获取到锁,lock方法返回。如果tryAcquire方法返回false,则会调用addWaiter()方法把当前线程加到同步队列尾部,然后调用acquireQueued()方法,acquireQueued方法内部有个死循环,直到当前线程获取到锁才会跳出循环返回。NonfairSync的tryAcquire方法内部调用Sync的nonfairTryAcquire()方法,实现是先判断当前state的值是否等于0,如果等于0,表示当前锁还没被任何线程获得,则CAS设置state,由0设置成1,如果设置成功,则把当前线程设置为获取独占锁的线程,返回true,否则返回false。如果不等于0,且当前线程就是之前获取到锁的线程,则让state的值加1,返回true。如果当前线程不是之前获取到锁的线程,则返回false。

假如是公平实现的话,则会调用FairSync的lock()方法,lock()方法内部就是调用acquire(1)。还是先调用tryAcquire方法,然后再根据tryAcquire方法的返回值决定是否继续调用addWaiter()方法、acquireQueued()方法。FairSync的tryAcquire方法实现和NonfairSync的tryAcquire方法实现有一点不同,那就是如果state等于0,会先判断在同步队列中当前节点是否有前驱节点,如果有,表示有别的线程比当前线程更早地请求获取锁,为了保证公平性,直接返回false。如果没有,才会去CAS设置state,等等。

tryLock()方法,不管是非公平实现,还是公平实现,逻辑都是一样的。内部就是调用Sync的nonfairTryAcquire()方法,参数是1。先判断当前state值是否等于0,如果等于0,则CAS设置state,由0设置成1,如果设置成功,则把当前线程设为获取独占锁的线程,返回true,否则返回false。如果state值不等于0,则判断获取锁的线程是否是当前线程,如果是,则把state值加1,返回true,否则返回false。

unlock()方法,不管是非公平实现,还是公平实现,逻辑都是一样的。内部就是调用Sync实例的release()方法,参数是1。release()方法是AQS的方法,内部先调用tryRelease()方法,tryRelease()方法也是AQS的方法,但是AQS没有实现,需要具体实现类实现,这里由Sync实现。tryRelease()方法会先判断当前线程是不是获得锁的线程,如果不是的话,会抛IllegalMonitorStateException异常。如果是的话,就把state的值减1,如果state值变为0,则把获取独占锁的线程设置为null,返回true,否则返回false。

ReentrantLock是AQS的独占式实现,Semaphore是AQS的共享式实现。

Semaphore用法示例:

    public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2); try {
semaphore.acquire();
System.out.println("执行业务操作");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}

AQS

AQS,队列同步器,简称同步器,是用来构建锁,如ReentrantLock、ReentrantReadWriteLock,或者其他同步组件,如CountDownLatch、Semaphore的基础框架。它使用一个int类型的成员变量表示同步状态,通过内置的先进先出队列完成线程的排队工作。同步器既支持独占式获取同步状态,也支持共享式获取同步状态,因此可实现不同类型的锁和同步组件。如ReentrantLock是独占式锁,CountDownLatch是共享式锁。

同步器的设计基于模板方法模式。在锁或者同步组件中,创建一个静态内部类,继承同步器并重写它的某些方法来管理同步状态,这些方法如果要修改同步状态,需要调用同步器提供的三个final方法:getState()、setState(int newState)和compareAndSetState(int expect, int update)方法来进行操作,这3个方法能保证状态的改变是安全的。锁或者同步组件对外暴露的获取锁、释放锁的方法会调用同步器的模板方法,这些模板方法又会调用我们重写的方法。可以重写的方法有:

boolean tryAcquire(int arg):独占式获取同步状态。

boolean tryRelease(int arg):独占式释放同步状态。

int tryAcquireShared(int arg):共享式获取同步状态。注意,返回值是int类型,不是boolean类型。返回值大于等于0,表示获取成功。否则,表示获取失败。

boolean tryReleaseShared(int arg):共享式释放同步状态。

boolean isHeldExclusively():

Lock接口和ReadWriteLock接口的更多相关文章

  1. 7.ReadWriteLock接口及其实现ReentrantReadWriteLock

    Java并发包的locks包里的锁基本上已经介绍得差不多了,ReentrantLock重入锁是个关键,在清楚的了解了同步器AQS的运行机制后,实际上再分析这些锁就会显得容易得多,这章节主讲另外一个重要 ...

  2. Java并发ReadWriteLock接口

    java.util.concurrent.locks.ReadWriteLock接口允许一次读取多个线程,但一次只能写入一个线程. 读锁 - 如果没有线程锁定ReadWriteLock进行写入,则多线 ...

  3. ReadWriteLock 接口详解

    ReadWriteLock 接口详解 这是本人阅读ReadWriteLock接口源码的注释后,写出的一篇知识分享博客 读写锁的成分是什么? 读锁 Lock readLock(); 只要没有写锁,读锁可 ...

  4. 转】C#接口-显式接口和隐式接口的实现

    [转]C#接口-显式接口和隐式接口的实现 C#中对于接口的实现方式有隐式接口和显式接口两种: 类和接口都能调用到,事实上这就是“隐式接口实现”. 那么“显示接口实现”是神马模样呢? interface ...

  5. JDBC的使用(二):PreparedStatement接口;ResultSet接口(获取结果集);例题:SQL注入

    ResultSet接口:类似于一个临时表,用来暂时存放数据库查询操作所获得的结果集. getInt(), getFloat(), getDate(), getBoolean(), getString( ...

  6. 比较器:Compare接口与Comparator接口区别与理解

    一.实现Compare接口与Comparator接口的类,都是为了对象实例数组排序的方便,因为可以直接调用 java.util.Arrays.sort(对象数组名称),可以自定义排序规则. 不同之处: ...

  7. 集合中Set接口与Collection接口,常用子类TreeSet,HashSet.

    Set接口与List接口的不同之处在于: 不允许有重复的数据. 定义如下: public interface Set<E>extends Collection<E> 主要方法与 ...

  8. Callable接口、Runable接口、Future接口

    1. Callable与Runable区别 Java从发布的第一个版本开始就可以很方便地编写多线程的应用程序,并在设计中引入异步处理.Thread类.Runnable接口和Java内存管理模型使得多线 ...

  9. 转载-- http接口、api接口、RPC接口、RMI、webservice、Restful等概念

     http接口.api接口.RPC接口.RMI.webservice.Restful等概念 收藏 Linux一叶 https://my.oschina.net/heavenly/blog/499661 ...

随机推荐

  1. java中是如何解决编码问题的,比如char类型的对象是如何存储的呢?

    主题句:每个编码形式将字符从字符集转换为编码数据. 说白了一个代码点就是一个Unicode字符.代码单元就是代码点的集合. 字符视图 要了解字符集标准,您必须能区分三种不同的字符视图: 字符集(字符的 ...

  2. 形式化验证工具(PAT)羊车门代码学习

    首先介绍一下PAT工具,下图是PAT工具的图标 PAT工具全称是Process Analysis Toolkit,可以做一些简单的验证. 今天我们分析一下例子里面的Monty Hall Problem ...

  3. Sql Server 2008 Management studio安装教程

    Sql Server 2008 Management studio安装教程     以下介绍Visual Studio 2010已安装后,sql server 2008 management stud ...

  4. 《Maven实战》笔记-3-Maven仓库

    一.Maven仓库的分类 1.本地仓库 一般来说,在Maven项目目录下,没有诸如lib/这样用来存放依赖文件的目录. 要自定义本地仓库目录地址时,可以编辑文件~/.m2/setting.xml,设置 ...

  5. CSS+DIV网页样式布局实战从入门到精通 中文pdf扫描版

    CSS+DIV网页样式布局实战从入门到精通通过精选案例引导读者深入学习,系统地介绍了利用CSS和DIV进行网页样式布局的相关知识和操作方法. 全书共21章.第1-5章主要介绍网页样式布局的基础知识,包 ...

  6. Windows 安装 mysql-5.7.12-winx64(CommunityServer) 备忘

    下载后文件名 mysql-5.7.12-winx64.msi 以为还是会和以往一样,一路next傻瓜式安装并完成设置,然后启动运行,但实际情况不是,感觉就是一解压过程,然后点击The MySQL Co ...

  7. ubuntu - 14.04,如何使用鼠标右键菜单在shell中打开选择项目?

    在shell中执行:“sudo apt-get install nautilus-open-terminal”,随后重新启动系统,在要打开的文件夹上面鼠标右键,会有一个菜单项目“在终端中打开”,点击后 ...

  8. 基于注解的AOP配置

    配置文件 spring配置文件中的约束 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ...

  9. css实现点点点效果

    @keyframes dotDotDoting{ 0% { width 0px margin-right 15px } 25% { width 0px margin-right 15px } 50% ...

  10. Linux配置国内的Yum源

    因为Linux默认的yum源是国外的源,所以会有卡顿,缓慢的情况.而国内的Yum源相对速度较快,现在也比较成熟,所以给Linux更换国内Yum源是一个很好的选择. 1.  备份(备份之前需要yum i ...