Lock锁原理解析【Android进阶】

news2024/11/23 21:47:38

Lock锁原理解析【Android进阶】

Lock简介

Lock接口位于J.U.C下locks包内,其定义了Lock应该具备的方法。

Lock 方法签名:

  • void lock():获取锁(不死不休,拿不到就一直等)
  • boolean tryLock():获取锁(浅尝辄止,拿不到就算了)
  • boolean tryLock(long time, TimeUnit unit) throws InterruptedException:获取锁(过时不候,在一定时间内拿不到锁,就算了)
  • void lockInterruptibly() throws InterruptedException:获取锁(任人摆布,xxx)
  • void unlock():释放锁
  • Condition newCondition():获得Condition对象

synchronized和lock的区别:

  1. synchronized是java关键字,是用c++实现的;而lock是用java类,用java可以实现
  2. synchronized可以锁住代码块,对象和类,但是线程从开始获取锁之后开发者不能进行控制和了解;lock则用起来非常灵活,提供了许多api可以让开发者去控制加锁和释放锁等等。

写个Demo

static Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {         lock.lock();//其他没拿到锁的卡住不动         Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("start to get lock Interruptibly");
                lock.unlock(); //看看会发生什么,注释掉再看看
                lock.lock();
                System.out.println("拿到锁");
                lock.unlock();
                System.out.println("释放锁");
            }
        });
        thread.start();         Thread.sleep(3000);
        lock.unlock();
    }

我们自己来手写一下lock接口的tryLock()、lock()和unLock()方法,实现我们自己的myLock。

public class MyLock implements Lock {
    //多并发调用  0-未占用 大于0-占用
    AtomicInteger state = new AtomicInteger();     Thread ownerThread = new Thread();     //等待锁的队列
    LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue();     @Override
    public void lock() {
        if (!tryLock()) {  //先抢锁,所以是非公平锁
            //没拿到锁,放到队列中去进行排队
            waiters.add(Thread.currentThread());
            //等待被唤醒
            for (; ; ) {
                if (tryLock()) {  //非公平锁情况下,唤醒过来继续获取锁
                    waiters.poll(); //获取锁成功把自己从队列中取出来
                    return;
                } else    //获取锁失败
                    LockSupport.park();  //线程阻塞
            }
        }
    }     @Override
    public boolean tryLock() {
        if (state.get() == 0) { //如果锁没被占用
            if (state.compareAndSet(0, 1)) {  //如果成功拿到锁
                ownerThread = Thread.currentThread();   //占用锁线程改为当前线程
                return true;
            }
        }
        return false;
    }     @Override
    public void unlock() {         if (ownerThread != Thread.currentThread())  //占用锁线程不是当前线程无法释放锁
            throw new RuntimeException("非法调用,当前锁不属于你");         if (state.decrementAndGet() == 0)  //如果成功释放锁
            ownerThread = null;  //占用锁线程置空
        //通知其他线程
//        Thread thread = null;
//
//        while ((thread = waiters.peek()) != null)
//            LockSupport.unpark(thread);
        Thread thread = waiters.peek(); //获取队列头部线程,线程还留在队列中
        if (thread != null) {
            LockSupport.unpark(thread); //取消阻塞
        }
    }     @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }     @Override
    public Condition newCondition() {
        return null;
    }     @Override
    public void lockInterruptibly() throws InterruptedException {     }
}

几个注意点:

  • 锁的占用状态state是AtomicInteger类型,底层原理是CAS,这是为了保证在多并发情况下线程安全问题;
  • 当线程1释放锁成功时,获取队列头部线程但并不取出,因为非公平锁模式下,队列头部线程不一定能获取到锁;
  • LockSupport的park()和unPark()方法是native方法,可以阻塞,唤醒线程;

Lock默认是非公平锁,上面实现的也是非公平锁,小伙伴们可以试一试。

公平锁和非公平锁区别:

先等待先获取锁是公平锁;先等待也不一定先获取锁,可能被突然到来的线程获取到是非公平锁;

公平锁的实现:

  @Override
    public void lock() {
       checkQueue();//线程来的时候先不获取锁,而是先检查队列中有没有等待的线程,如果有,直接放入队列,如果没有,再去获取锁
        if (!tryLock()) {  //先抢锁,所以是非公平锁
            //没拿到锁,放到队列中去进行排队
            waiters.add(Thread.currentThread());
            //等待被唤醒
            for (; ; ) {
                if (tryLock()) {  //非公平锁情况下,唤醒过来继续获取锁
                    waiters.poll(); //获取锁成功把自己从队列中取出来
                    return;
                } else    //获取锁失败
                    LockSupport.park();  //线程阻塞
            }
        }
    }

lock源码

在阅读源码的成长的过程中,有很多人会遇到很多困难,一个是源码太多,另一方面是源码看不懂。在阅读源码方面,我提供一些个人的建议:

  1. 第一个是抓主舍次,看源码的时候,很多人会发现源码太长太多,看不下去,这就要求我们抓住哪些是核心的方法,哪些是次要的方法。当舍去次要方法,就会发现代码精简和很多,会大大提高我们阅读源码的信心。
  2. 第二个是不要死扣,有人看源码会一行一行的死扣,当看到某一行看不懂,就一直停在那里死扣,知道看懂为止,其实很多时候,虽然看不懂代码,但是可以从变量名和方法名知道该代码的作用,java中都是见名知意的。

接下来进入阅读lock的源码部分,在lock的接口中,主要的方法如下:

public interface Lock {



    // 加锁



    void lock()// 尝试获取锁



    boolean tryLock();



    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;



    // 解锁



    void unlock();



}

在lock接口的实现类中,最主要的就是ReentrantLock,来看看ReentrantLocklock()方法的源码:

    // 默认构造方法,非公平锁



    public ReentrantLock() {



        sync = new NonfairSync();



    }



 



    // 构造方法,公平锁



    public ReentrantLock(boolean fair) {



        sync = fair ? new FairSync() : new NonfairSync();



    }



 



    // 加锁



    public void lock() {



        sync.lock();



    }

在初始化lock实例对象的时候,可以提供一个boolean的参数,也可以不提供该参数。提供该参数就是公平锁,不提供该参数就是非公平锁。
在这里插入图片描述

以上就是Android开发中的Lock的解析,更多有关乐观锁,显示锁,内置锁的使用或者Android核心技术可以参考《Android核心技术手册》点击查看获取!

总结

  1. lock的存储结构:一个int类型状态值(用于锁的状态变更),一个双向链表(用于存储等待中的线程)
  2. lock获取锁的过程:本质上是通过CAS来获取状态值修改,如果当场没获取到,会将该线程放在线程等待链表中。
  3. lock释放锁的过程:修改状态值,调整等待链表。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/158304.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

13.0、Linux-安装Tomcat、以及防火墙常用命令

13.0、Linux-安装Tomcat、以及防火墙常用命令 防火墙常用命令在本文最下方-> 安装 Tomcat -> 第一步&#xff1a;进入官网下载 Tomcat8 -> Apache Tomcat - Apache Tomcat 8 Software Downloads 第二步&#xff1a;将下载好的文件 通过 Xshell 和 Xftp 工具上传到 L…

前端面试题——React重点

文章目录React相关问题1. 函数式组件与Class组件有什么不同&#xff1f;2. 说说React的fiber架构&#xff1f;3. 协调4. 虚拟DOM (Virtual DOM)React相关问题 1. 函数式组件与Class组件有什么不同&#xff1f; 答&#xff1a; ① 函数式组件不需要继承&#xff0c;直接 “fun…

YUV 文件读取、显示、缩放、裁剪等操作教程

系列文章目录 文章目录系列文章目录前言一、Chroma subsampling二、读取 YUV 文件2.1 准备工作2.2 Planar 模式2.2.1 YUV420P 格式读取2.2.2 YUV422P 格式读取2.2.3 NV21 格式读取2.2.4 NV12 格式读取2.2.5 小结2.3 Packed 模式2.3.1 YUYV 格式读取2.3.2 小结三、SDL 显示 YUV …

自动化测试是什么?为什么要做自动化测试?

自动化测试是什么&#xff1f;相信对软件测试有一定了解的朋友都不会感到陌生。自动化测试正如字面上理解得那样&#xff0c;是一种自动完成测试工作的测试方式。虽然是说的自动化&#xff0c;但是也是需要测试员手动编写代码去完成测试工作。那么&#xff0c;为什么要做自动化…

【链表】leetcode19.删除链表的倒数第N个节点(C/C++/Java/Js)

leetcode19.删除链表的倒数第N个节点1 题目2 思路3 代码3.1 C版本3.2 C版本3.3 Java版本3.4 JavaScript版本4 总结1 题目 题源链接 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2…

微服务了解

1、什么是微服务 微服务就是根据业务功能模块把一个单体的应用拆分成许多个独立的项目&#xff0c;每个项目完成一部分的业务功能&#xff0c;然后独立开发和部署。这些独立的项目就成为一个微服务。进而构成一个服务集群。 微服务个人理解就是将之前的单一服务架构&#xff…

【Python】词云之wordcloud参数全解析

写了一半&#xff0c;明天继续 目录一基础用法二、WordCloud类形参说明2.1 常用参数2.11 字体 font_path2.12 画布尺寸 width、hight2.13 比例&#xff08;缩放&#xff09;scale2.14 颜色 colormap2.15 颜色color_func2.16 组合频率collocations2.17 遮罩&#xff08;蒙版&…

SpringBoot(57) 整合Plumelog实现日志查询

文章目录一、前言二、docker-compose一键搭建日志服务docker-compose.yml三、SpringBoot整合Plumelog1、pom.xml2、application.yml3、logback-spring.xml4、启动项目记录日志四、本文案例demo源码一、前言 Plumelog 一个简单易用的java分布式日志组件 https://gitee.com/plum…

机器学习笔记之深度信念网络(三)贪心逐层预训练算法

机器学习笔记之深度信念网络——贪心逐层预训练算法引言回顾&#xff1a;深度信念网络的结构表示回顾&#xff1a;RBM\text{RBM}RBM叠加思想贪心逐层预训练算法引言 上一节介绍了深度信念网络模型的构建思想&#xff0c;本节将介绍后验概率求解——贪心逐层预训练算法。 回顾…

窥探Swift源码下的Array

本文字数&#xff1a;6730字预计阅读时间&#xff1a;15 分钟用最通俗的语言&#xff0c;描述最难懂的技术前情提要我在之前的文章一次遍历导致的崩溃中提到了&#xff0c;如果有机会会把相关的Swift集合源码阅读。首先对自己的开发和知识储备会有进一步的升华&#xff0c;另外…

Redis -- IO多路复用及redis6的多线程

都知道redis是通过单线程io多路复用来避免并发问题的&#xff0c;然后在redis6的时候redis引入了多线程&#xff0c;这里就来详细说说IO多路复用模型以及redis的多线程。 Redis 的 I/O 多路复用模型有效的解决单线程的服务端&#xff0c;使用不阻塞方式处理多个 client 端请求…

optional妙用解决NullPointerException

背景 作为一个java程序员&#xff0c;我们在日常开发中经常会碰到一个臭名昭著空异常&#xff0c;所有程序员都经历过的痛点&#xff0c;空指针异常&#xff08;NullPointerException&#xff09; java8中出现的Optional是java开始迈向函数式编程的一大步&#xff0c;也有效的…

实验 4 图像复原与重建

目录一、实验目的二、实验例题1. 噪声模型2. 只存在噪声的滤波——空间滤波3. 退化函数建模4. 存在线性退化和噪声的滤波(1) 逆滤波(2) 最小均方误差(维纳)滤波附录一、实验目的 了解一些常用随机噪声的生成方法。掌握根据指定退化函数对图像进行退化的方法。掌握当模糊图像只…

十一、Gtk4-Instance Initialization and destruction

文本文件编辑器(tfe)的新版本将在本节和以下四节中编写。它是tfe5。与之前的版本相比&#xff0c;有很多变化。它们位于两个目录中&#xff0c;src/tfe5和src/tfetextview。 1 封装 我们将C源文件分为两部分。但就封装而言&#xff0c;这还不够。 tfe.c包含除Tfe TextView之…

【Linux】线程控制

目录&#x1f308;前言&#x1f338;1、Linux线程控制&#x1f361;1.1、创建线程(pthread_create)&#x1f362;1.2、通过指令查看线程PID和LWP&#x1f367;1.3、获取线程ID(pthread_self)&#x1f368;1.4、线程终止(pthread_exit/cancel)&#x1f368;1.5、线程等待(pthrea…

一文读懂PCB阻焊工艺

PCB阻焊油墨根据固化方式&#xff0c;阻焊油墨有感光显影型的油墨&#xff0c;有热固化的热固油墨&#xff0c;还有UV光固化的UV油墨。而根据板材分类&#xff0c;又有PCB硬板阻焊油墨&#xff0c;FPC软板阻焊油墨&#xff0c;还有铝基板阻焊油墨&#xff0c;铝基板油墨也可以用…

力扣算法(Java实现)—数组入门(11题)

文章目录1.删除排序数组中的重复项2.买卖股票的最佳时机 II3.旋转数组4.存在重复元素5.找出只出现一次的元素6.两个数组的交集7.移动零8.加一9.两数之和10.有效的数独11.旋转图像&#x1f48e;&#x1f48e;&#x1f48e;&#x1f48e;&#x1f48e; 更多资源链接&#xff0c;欢…

【手写 Vue2.x 源码】第十八篇 - 根据 render 函数,生成 vnode

一&#xff0c;前言 上篇&#xff0c;介绍了render 函数的生成&#xff0c;主要涉及以下两点&#xff1a; 使用 with 对生成的 code 进行一次包装将包装后的完整 code 字符串&#xff0c;通过 new Function 输出为 render 函数 本篇&#xff0c;根据 render 函数&#xff0c…

linux系统中QT里面的视频播放器的实现方法

大家好&#xff0c;今天主要和大家聊一聊&#xff0c;如何使用QT中视频播放器的方法。 目录 第一&#xff1a;视频播放器基本简介 第二&#xff1a;视频播放器头文件说明 第三&#xff1a;源文件的具体实现方法 第四&#xff1a;运行效果显示 第一&#xff1a;视频播放器基本…

ADS振铃仿真

目录 无振铃时的原理图 无振铃时的Vout和VL输出波形 ​LineCalc对微带线阻抗的计算结果 将微带线线宽Width统一由116改为130 将微带线线宽Width统一由116改为80 将微带线TL9线宽由116改为300 将微带线TL9线宽由116改为50 本文介绍了微带线线宽变化时100MHz信号的反射现象…