敞开生长之旅!这是我参与「日新方案 2 月更文应战」的第 8 天,点击检查活动概况

前语

之前提到过有大佬给了建议,让我创立一个同享的ReentrantLock来处理快速多次点击检查权限时分(当然情形是更为杂乱,有其他UI操作同时进行(切换播映区域),应该还有一个进度条DIalog也在展示占用屏幕)重复弹出弹窗。

但我对这个类不是很熟悉,之前只是看过介绍该知识点的文章,还没开发中运用操作过,所以咱们先看看ReentrantLock的简单运用。

正篇

ReentrantLock概况

ReentrantLock(翻译过来叫可重入锁)是基于AQS进行的完成。

原理基础

其间AQS的全称为AbstractQueuedSynchronizer(直译为抽象行列同步器),是阻塞式锁和同步器工具的框架,用一个state来表示锁状况,经过子类CAS(确保原子性)去操作,内部维护了一个等候行列进行排队,运用条件变量来完成等候、唤醒,支持多个条件变量功用,咱们的ReentrantLock只是其间的一种完成办法,是AQS排他形式(独占锁),此外还有运用AQS同享形式的ReentrantReadWriteLock中的读锁。

布景材料

在jdk1.6之前,咱们运用锁完成同步运用的是synchronized关键字,但是synchronized的完成原理是调用操作系统函数来完成加锁/解锁,而一旦触及操作系统的函数,那么代码履行的功率就会变低,所以,运用synchronized关键字来完成加/解锁就被称为重量级锁。
为了改进这一状况,Doug Lea就写了ReentrantLock锁,这种锁分状况在jvm层面和操作系统层面完成加锁/解锁的过程,因而代码履行功率明显提高,后来Sun公司在jdk1.6以后也改进了synchronized,使得synchronized的履行功率和ReentrantLock差不多,乃至更好,但是因为ReentrantLock能够直接代码操作加锁/解锁,可中止获取锁等特性,因而运用的比较多。

ReentrantLock运用

当有多个线程需要对同一个同享变量进行操作的时分就需要考虑上锁了,下面咱们用一条样例代码说明:

线程1与线程2抢锁的样例代码如下:

package com.example.weekreference;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
    public static void main(String[] args){
        final ReentrantLock lock = new ReentrantLock(true);
        Thread LockTest1 = new Thread("LockTest1"){
            @Override
            public void run() {
                lock.lock();
                lockTest();
                lock.unlock();
            }
        };
        Thread LockTest2 = new Thread("LockTest2"){
            @Override
            public void run() {
                lock.lock();
                lockTest();
                lock.unlock();
            }
        };
        LockTest1.start();
        LockTest2.start();
    }
    /**
     * 打印线程名,以及线程休眠2秒后打印日志
     */
    public static void lockTest(){
        System.out.println(Thread.currentThread().getName());
        try {
            Thread.sleep(2000);
            System.out.println(" LockTest-------end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

咱们能够右击办法名旁边的绿色三角形挑选覆盖率测试运转(Run “…” with Coverage)

安卓基础开发——ReentrantLock的简单使用
等候运转完成,咱们能看到线程的打印

安卓基础开发——ReentrantLock的简单使用
其间lock()办法就是加锁,unlock()办法就是开释锁。

安卓基础开发——ReentrantLock的简单使用

安卓基础开发——ReentrantLock的简单使用
如果咱们把上面样例代码的ReentrantLock目标的lock办法和unlock办法去除,再次运转打印,咱们就发现:

安卓基础开发——ReentrantLock的简单使用
线程1和线程2都会正常运转,但线程休眠办法打印的日志会靠后,而加锁和解锁时,咱们线程会顺次等候,线程2会等线程1操作完开释锁后,才去运转。

而其间咱们去完成ReentrantLock初始化时,咱们传入了一个boolean值,这是用来挑选是公正锁仍对错公正锁的,默认对错公正锁,有关公正锁与非公正锁(Java中是抢锁时区分)简要意义如下:

  • 公正锁 – 依照恳求锁的次序来获取锁
  • 非公正锁 – 是否能获取到锁与恳求锁的次序无关

安卓基础开发——ReentrantLock的简单使用
下面再介绍几个常用的办法。

常用办法

  • isLocked()
    是否持有锁

  • isFair()
    是否是公正锁

  • hasQueuedThreads()
    锁等候行列中是否有线程在等候锁

  • hasQueuedThread(Thread thread)
    指定的线程是否存在于锁等候行列中

  • getQueueLength()
    锁等候行列中的等候线程数

  • isHeldByCurrentThread()
    当时线程是否持有此锁

  • getHoldCount()
    当时线程持有此锁的数量(是数量,而不是次数。比如嵌套锁时,当时线程持有此锁的数量就可能会大于 1)

总结

这个部分内容我自己实践的也不行,后续有新的关于ReentrantLock方面的会及时补充到这篇文章,主要本次未解释自旋锁和阻塞锁,此外前语提到的同享状况,我经过查询材料得知AQS其实还有ReentrantReadWriteLock(读写锁,读锁是同享锁)和CountDownLatch(完全只用了AQS同享形式),本篇就讲的是独占锁(排他),而不是同享锁(读写锁):

final ReentrantLock lock = new ReentrantLock(true);
final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
final CountDownLatch countDownLatch = new CountDownLatch(1);

安卓基础开发——ReentrantLock的简单使用
这几个后续再开新文章去记录学习过程。