我的编程空间,编程开发者的网络收藏夹
学习永远不晚

AndroidLock锁实现原理详细分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

AndroidLock锁实现原理详细分析

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的区别

  • synchronized是java关键字,是用c++实现的;而lock是用java类,用java可以实现
  • 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源码

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

  • 第一个是抓主舍次,看源码的时候,很多人会发现源码太长太多,看不下去,这就要求我们抓住哪些是核心的方法,哪些是次要的方法。当舍去次要方法,就会发现代码精简和很多,会大大提高我们阅读源码的信心。
  • 第二个是不要死扣,有人看源码会一行一行的死扣,当看到某一行看不懂,就一直停在那里死扣,知道看懂为止,其实很多时候,虽然看不懂代码,但是可以从变量名和方法名知道该代码的作用,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的参数,也可以不提供该参数。提供该参数就是公平锁,不提供该参数就是非公平锁。

总结

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

到此这篇关于Android Lock锁实现原理详细分析的文章就介绍到这了,更多相关Android Lock锁内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

AndroidLock锁实现原理详细分析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

AndroidLock锁实现原理详细分析

这篇文章主要介绍了AndroidLock锁实现原理,Lock接口的实现类提供了比使用synchronized关键字更加灵活和广泛的锁定对象操作,而且是以面向对象的方式进行对象加锁
2023-02-17

Vue组件实现原理详细分析

这篇文章主要介绍了Vue组件基础操作,组件是vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互进行直接的引用
2023-01-18

Vue组件的实现原理详细分析

在日常业务开发中我们会经常封装一些业务组件,下面这篇文章主要给大家介绍了关于Vue组件的实现原理,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
2023-01-03

redis分布式锁的实现原理实例分析

这篇文章主要介绍了redis分布式锁的实现原理实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇redis分布式锁的实现原理实例分析文章都会有所收获,下面我们一起来看看吧。首先,为了确保分布式锁可用,我们至
2023-06-29

Redis集群原理详细分析

Redis集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。Redis集群通过分区来提供一定程度的可用,即使集群中有一部分节点失效或者无法进行通讯,集群也可以继续处理命令请求
2022-12-19

详解Redis分布式锁的原理与实现

目录前言使用场景为什么要使用分布式锁如何使用分布式锁流程图分布式锁的状态分布式锁的特点分布式锁的实现方式(以redis分布式锁实现为例)总结前言在单体应用中,如果我们对共享数据不进行加锁操作,会出现数据一致性问题,我们的解决办法通常是加锁
2022-06-23

MySQL读写分离原理详细解析

目录一、读写分离的概念二、引入中间件MyCat三、http://www.cppcns.comMyCat服务端口和管理端口一、读写分离的概念读写分离是基于主从复制来实现的。在实际的应用环境中,肯定是读操作多,就像我们在电商平台上去购买东西,
2022-07-14

Gochannel实现原理分析

Channel是go语言内置的一个非常重要的特性,也是go并发编程的两大基石之一,下面这篇文章主要给大家介绍了关于Go中channel的相关资料,需要的朋友可以参考下
2023-05-14

MySQL的主从复制原理详细分析

目录前言一、主从复制概念二、读写分离的概念三、主库和从库1. 主库2. 从库四、主从复制的流程五、主从复制效果展示前言在实际生产环境中,如果对mysql数据库的读和写都在一台数据库服务器中操作,无论是在安全性、高可用性,还是高并发等各个方
2022-07-14

HTML固定定位原理的详细分析

深入解析HTML固定定位的实现原理,需要具体代码示例引言:在Web开发中,我们经常会遇到需要将某个元素固定在页面的某个位置不动,随着页面的滚动而保持位置不变的需求。这就是HTML固定定位。本文将深入解析HTML固定定位的实现原理,并提供具
HTML固定定位原理的详细分析
2024-01-20

Java@Autowired注解底层原理详细分析

@Autowired注解可以用在类属性,构造函数,setter方法和函数参数上,该注解可以准确地控制bean在何处如何自动装配的过程。在默认情况下,该注解是类型驱动的注入
2022-11-13

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录