深入理解独占锁ReentrantLock类锁

ReentrantLock介绍【1】ReentrantLock是一种基于AQS框架的应用实现 , 是JDK中的一种线程并发访问的同步手段,它的功能类似于synchronized是一种互斥锁,可以保证线程安全 。
【2】相对于 synchronized,ReentrantLock具备如下特点:
1)可中断2)可以设置超时时间3)可以设置为公平锁4)支持多个条件变量5)与 synchronized 一样,都支持可重入ReentrantLock问题分析【1】ReentrantLock公平锁和非公平锁的性能谁更高?
1)那肯定是非公平锁,但是为什么是非公平更高呢?
2)因为涉及到了线程的park()与unpark()操作,不管是ReentrantLock还是synchronized,都在避免这些操作 。
?。?)如ReentrantLock的非公平同步器在得不到锁的情况下,即将要进入之前会再加一次锁,生成节点之后又会加一次锁,把节点放入队列之后又会加一次锁,最终迫不得已才会进行park()操作 。
?。?)如synchronized,在生成monitor的过程之中也会多次尝试加锁,避免monitor的生成 。
3)为什么要避免呢?这就涉及到线程的概念了 。
?。?)因为park()与unpark()操作涉及到了线程的上下文切换 , 同时又涉及到了时间片轮转机制 。
?。?)线程上下文切换,需要将旧的线程资源保存回内存【保存执行到了哪一步,需要什么东西】,将新的线程的资源加载入CPU , 让新线程具备执行的资源并开始执行 。但是这些操作都是需要花费时间的 , 会消耗一部分时间片的资源 。如(这里仅仅只是举例说明),一个时间片本来就是50s,你拿到的时候花费了一定的时间(如10s)进行上下文切换,现在刚执行不到5s,你又要进行一次切换(又要花费10s) 。那下一个拿到时间片的线程会不会还是会继续切换呢?而且你要下次运行就又要等时间片了 。
?。?)所以说,本质上非公平机制是为了让持有CPU的线程尽可能多的做有用的任务,减少上线下文切换带来的开销,毕竟时间片来之不易 , 本身就是从众多线程之中好不容易分配得来的 。
ReentrantLock的使用【1】使用模板
Lock lock = new ReentrantLock();//加锁lock.lock();try {// 临界区代码// TODO 业务逻辑:读写操作不能保证线程安全} finally {// 解锁,放置在这里的原因是保证异常情况都不能干扰到解锁逻辑lock.unlock();}【2】可重入的尝试
public class ReentrantLockDemo {public static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {method1();}public static void method1() {lock.lock();try {log.debug("execute method1");method2();} finally {lock.unlock();}}public static void method2() {lock.lock();try {log.debug("execute method2");method3();} finally {lock.unlock();}}public static void method3() {lock.lock();try {log.debug("execute method3");} finally {lock.unlock();}}}【3】中断机制尝试
【深入理解独占锁ReentrantLock类锁】进行说明:这里面其实是main线程先获得了锁 , 所以t1线程其实是先进入队列里面 , 然后在main线程里面将t1设置为了中断 。当main线程释放锁的时候,t1去加锁,发现自己被中断了,所以抛出中断异常,退出加锁 。【其实这个中断加锁 , 怎么说,就是可以让失去加锁的权利,但是不影响你去排队】
@Slf4jpublic class ReentrantLockDemo {public static void main(String[] args) throws InterruptedException {ReentrantLock lock = new ReentrantLock();Thread t1 = new Thread(() -> {log.debug("t1启动...");try {lock.lockInterruptibly();try {log.debug("t1获得了锁");} finally {lock.unlock();}} catch (InterruptedException e) {e.printStackTrace();log.debug("t1等锁的过程中被中断");}}, "t1");lock.lock();try {log.debug("main线程获得了锁");t1.start();//先让线程t1执行Thread.sleep(10000);t1.interrupt();log.debug("线程t1执行中断");} finally {lock.unlock();}}}【4】锁超时尝试
@Slf4jpublic class ReentrantLockDemo {public static void main(String[] args) throws InterruptedException {ReentrantLock lock = new ReentrantLock();Thread t1 = new Thread(() -> {log.debug("t1启动...");//超时try {// 注意: 即使是设置的公平锁 , 此方法也会立即返回获取锁成功或失败 , 公平策略不生效if (!lock.tryLock(1, TimeUnit.SECONDS)) {log.debug("等待 1s 后获取锁失败 , 返回");return;}} catch (InterruptedException e) {e.printStackTrace();return;}try {log.debug("t1获得了锁");} finally {lock.unlock();}}, "t1");lock.lock();try {log.debug("main线程获得了锁");t1.start();//先让线程t1执行Thread.sleep(2000);} finally {lock.unlock();}}}【5】条件变量的尝试
说明:
1)java.util.concurrent类库中提供Condition类来实现线程之间的协调 。调用Condition.await() 方法使线程等待,其他线程调用Condition.signal() 或 Condition.signalAll() 方法唤醒等待的线程 。

推荐阅读