Android开发-多线程之换个视角理解_乱序执行 android

4.2.2 锁的升级过程
  1. 一开始new 出锁对象时,还没有线程进入临界区,此时是无锁态。
  2. 有线程进入,则改成偏向锁,同时markword存入线程的id。
  3. 如果是同一线程则还是偏向锁。
  4. 当另外线程也进入临界区,请求锁对象,发现对象头已经有偏向锁了。产生了竞争!!那不好意思,先撤销偏向锁,然后两个线程通过CAS自旋的方式开始争抢锁对象,都往锁对象头里面写入自己线程栈的lock record。一旦有线程争抢成功,那么其他线程就会失败,此时锁对象变成了轻量级锁。
  5. 失败的线程会一直CAS循环下去,此间也可能还会有其他线程参与进来自旋。
  6. 当自旋次数超过一定值如10次,或者参与自旋的线程数太多。系统会进行干预。
  7. 这样干耗着会浪费CPU资源,所以干脆升级为重量级锁。其他线程全部进入mutex中的队列中去排队,线程进入wait或者block状态,不消耗CPU。
  8. 但synchronize修饰的是非公平的队列。

讲了这么多,synchronize的底层到底是怎么实现的?

其实还是 lock cmpxchg 指令。

4.3 volatile

volatile修饰变量后有两个作用: 1,内存可见性 线程间工作内存和主存实现了及时同步 2,防止指令重排 这对这个变量的操作被JMM加入内存屏障来保证指令不会乱序执行。

volatile到底是怎么解决指令重排的??

JVM层通过加入内存屏障,是一个逻辑实现,是jvm的要求规范而已,具体要看汇编语言。

  1. loadload 屏障 读
  2. storestore 屏障 写
  3. loadstore 屏障
  4. storeload 屏障

四个逻辑。 具体 就是在volatile读/写的前后加入内存屏障,保证顺序执行。内存屏障前后的指令不能重排序!

汇编层面: 最终就是调用了 lock: andl 指令。表示在寄存器中加0操作。

为什么这条指令能实现内存可见和禁止指令重排序??

内存可见性: 该指令能够将当前处理器对应缓存内容刷新到内存,并且是其他处理器的缓存失效。 重排序: 该指令本身就是内存屏障,它前面的指令和后面的指令都不能重排序。

4.4 CAS和原子操作
4.4.1 乐观锁和悲观锁
  • 悲观锁: 在访问共享资源的时候总是认为别人会来抢,所以只要访问临界区就直接上锁。通俗讲就是因为怕被抢,所以无脑上锁。比如用synchronize来对临界区上锁。
  • 乐观锁: 在访问共享资源时候,认为别的线程不会来抢资源。所以是“无锁”状态。但是可以通过CAS来保证数据的安全。ReentrantLock互斥锁(互斥别人,不互斥自己)
4.4.2 CAS

CAS(compare and swap): 比较并且交换。 目的: 在没有锁的状态下,可以保证多个线程对一个值的更新。

CAS实现思想:

  • E:拿到变量当前原始值(期望值)
  • V:计算的结果值
  • N:再次获取变量的值(当前值)

举个例子: 假设i=0,对i做++操作。 CAS的过程是这样的:

  1. 拿当i的前值 E=0;
  2. 计算结果值V=1;
  3. 再次拿i的当前,有可能如下情况: N=0;N=3(因为某个线程更改了)
  4. 如果E=N,表示没有被修改,我们可以更新,直接修改i=1。
  5. 如果E!=N,表示被修改过,我们这次修改就不能执行。然后我们在重头开始,再次比较,最终实现交换。

流程图:

- 实现的本质:

AtomicInteger内部就是通过CAS的方式来保证线程安全的。

内部会调用UnSafe类的方法。

private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();


/**
 * Atomically decrements by one the current value.
 *
 * @return the previous value
 */
public final int getAndDecrement() {
      //U表示 UnSafe类
    return U.getAndAddInt(this, VALUE, -1);
}



UnSafe类直接调用的是C++层的native方法compareAndSwapInt()

public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
        // while中 native 方法
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}

// native 方法
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);



通过源码发现compareAndSwapInt()最终会调用c++层的 Atomic::cmpxchg方法,有如下指令:

//_asm_  表示汇编指令
//LOCK_IF_MP: 如果是multi-processs 多处理器, 现在处理器都是多CPU的。
_asm_ volatile (LOCK_IF_MP(%4) "cmpxchg1 %1, (%3)")...//后面省略



如果是多个处理器则 进行lock。为什么? 多个CPU就会出现多线程同时执行,出现并发问题。

而CAS方法会直接通过汇编指令:lock cmpxchg 指令 来完成CAS真正的操作。 cmpxchg 这个指令也就体现了比较和交换的本质了。

所以, 现在的问题变成 lock cmpxhg 指令 是如何实现线程安全的? 写入的过程是没有原子性保证的。 由 lock 指令来保证原子性。 最终由硬件来支持,硬件怎么实现的啊? 好啦,到这里就可以啦。 硬件通过锁定北桥信号?(我也不清楚了啊)。

4.5 AQS(AbstractQueuedSynchronizer) 抽象队列同步器

高并发编程的核心: AQS。

里面通过维护一个volatile int state变量和一个存储线程的队列(双向链表)来实现同步的。 它本身是一个抽象的类,定义了同步模板方法。具体逻辑需要子类去继承实现。 可通过构造方法传入是否是公平锁。

线程通过CAS去获取state值,state初始值为0,那么拿到锁state=1。 后续如果再有线程进来,那么就封装成Node节点看,然后放到队列中去阻塞,知道之前的线程释放锁。 支持公平锁和非公平锁两种方式。

4.5.1 ReentrantLock和synchronize的区别?

synchronize: 最终要通过用户态到内核态的切换,但是有锁的升级优化。悲观锁

ReentrantLock(jdk1.5后新增的锁): 基于AQS同步机制,其实内部还是通过CAS来获取锁,不用到内核态,轻量级。更加灵活。属于 乐观锁。 需要自己手动try catch,在finally中释放锁。

文末福利

这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

一、架构师筑基必备技能

1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……

在这里插入图片描述

二、Android百大框架源码解析

1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程

在这里插入图片描述

三、Android性能优化实战解析
  • 腾讯Bugly:对字符串匹配算法的一点理解
  • 爱奇艺:安卓APP崩溃捕获方案——xCrash
  • 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
  • 百度APP技术:Android H5首屏优化实践
  • 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
  • 携程:从智行 Android 项目看组件化架构实践
  • 网易新闻构建优化:如何让你的构建速度“势如闪电”?

在这里插入图片描述

四、高级kotlin强化实战

1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》

  • 从一个膜拜大神的 Demo 开始
  • Kotlin 写 Gradle 脚本是一种什么体验?
  • Kotlin 编程的三重境界
  • Kotlin 高阶函数
  • Kotlin 泛型
  • Kotlin 扩展
  • Kotlin 委托
  • 协程“不为人知”的调试技巧
  • 图解协程:suspend

在这里插入图片描述

五、Android高级UI开源框架进阶解密

1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
在这里插入图片描述

六、NDK模块开发

1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习

在这里插入图片描述

七、Flutter技术进阶

1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)

在这里插入图片描述

八、微信小程序开发

1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……

在这里插入图片描述

全套视频资料:

一、面试合集

总结

最后对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司20年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 30
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值