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. Linux 内核与模块调试

    一.简介 内核开发比用户空间开发更难的一个因素就是内核调试艰难.内核错误往往会导致系统宕机,很难保留出错时的现场.调试内核的关键在于你的对内核的深刻理解.   二.方法总结 1)内核模块相关指令 ht ...

  2. SPOJ LCMSUM - LCM Sum

    题意是求: $\sum_{i = 1}^{n}lcm(i, n)$ $= \sum_{i = 1}^{n}\frac{ni}{gcd(i, n)}$ $= n\sum_{i = 1}^{n}\frac ...

  3. python3-列表中存储字典

    # Auther: Aaron Fan #示例1:#定义几个字典alien_0 = {"color":"green", "points":5 ...

  4. 导出Excel解决方案之一NOPI

    一.概要 导出Excel这个功能相信很多人都做过,但是实现这个功能解决方案有好几种,今天我未大家介绍一种比较新的,其实也不新了- -!它叫NPOI,可以完美操作EXCEl的导入和导出操作,让我们一起看 ...

  5. 总结:kathasis如何发送get请求获取数据

    1.进入前端页面,找到对应的模块,开始塞字段数据. 2.如果字段为基本类型,如String,比如website,则在前段界面,右击,inspect,找到对应的代码所处的jsp,跳转到该jsp,通过该j ...

  6. [译]Javascript中的错误信息处理(Error handling)

    本文翻译youtube上的up主kudvenkat的javascript tutorial播放单 源地址在此: https://www.youtube.com/watch?v=PMsVM7rjupU& ...

  7. Spring 特点

    IoC:豆浆和打针的例子.一个是主动的,一个是被动的.比如在spring里面我们需要对象了,提出需求,spring容器会把对象给你.(这就是IoC) AOP:

  8. c++内联 inline

    内联声明只是建议 ,不一定就会内联. http://www.voidcn.com/blog/u011327981/article/p-5006835.html

  9. 基于pythpn的深度学习 - 记录

    [基于pythpn的深度学习] 环境:    windows/linux-ubuntu    Tensorflow (基于anaconda)        *安装 (python3.5以上不支持)   ...

  10. C 语言 clock() 函数,例:计算多项式值

    C 语言 clock() 函数,例:计算多项式值 /** * clock(): 捕捉从程序开始运行到 clock() 被调用时所耗费的时间. * 这个时间单位是 clock tick, 即" ...