| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 移动开发 -> Android 开发多线程之换个视角理解 -> 正文阅读 |
|
[移动开发]Android 开发多线程之换个视角理解 |
写在前面理解多线程并发和锁的关键在于正确的理清当前代码正处在哪个线程的执行环境下。说白了,这个同步代码/同步方法谁都可以来执行的,关键是有没有其他人在用这个锁。 1 Thread如何理解1.1 Thread类与普通类的区别?线程与线程类是不同的概念。 线程是系统CPU资源调度的基本单元,它是一个抽象的概念。Thread类和其他别的类没有什么区别。它只是对线程有着“管理”作用。而真正执行的代码都是在run()方法中,或者外部传入的runnable中。 1.2 中介作用在线程的start方法之前,所有代码都在老线程上运行,调用start方法之后,内部会调用VMTthread.create,实际上真正在新线程上运行的只有run方法。这个角度理解,thread类只是一个中介,任务就是启动一个新线程来运行用户指定的runnable,而不关心是内部的还是外部传入的。 2 线程的状态
join()用来保证两个线程的顺序执行:
上面代码表示,只有当t1执行完毕,t2才会执行。 2.1 wait和notify是如何绑定的?通过同一个object对象。当一个线程调用某个object对象的wait方法时候,系统会在object中记录该请求,如果是多个线程调用则会有waitinglist,而当另外一个线程调用object的notify/notifyAll来唤醒一个/多个waitinglist中的线程。 2.2 线程调用wait()方法的条件?
也就说: 一个线程获得了对象object锁lock,它才可以调用wait方法,而调用wait方法后该线程会释放锁,从而可以让别的线程来获取。 2.4 线程什么时候会释放锁?
2.5 wait方法和sleep方法区别
3 Java内存模型的本质(重点)JMM java内存管理模型,提出了主存和线程本身的工作内存概念,如图:
一个单核CPU在一个线程上执行指令,如果需要切换线程它会把当前线程的执行现场保存到内存中去,方便后续恢复,然后清空PC计数器,加载新线程的指令地址。因此,单核CPU不存在同步的问题。 当多核CPU分别在执行自己线程指令时,如果存在共享同一个变量,那么就有可能存在竞争关系,因为每个CPU的核都有自己的本地缓存,而二者通信是通过内存中共享变量的方式来实现的。这就存在这个变量同步不及时的情况,所以需要同步。
4 同步相关问题(重点)4.1 重排序和happens-before4.1.1 重排序不管什么用到语言,我们写的代码最终都会转成汇编指令,而汇编指令与机器指令(如:01010)是一一对应的。因此当CPU在执行当前指令的时候处于读等待,CPU不工作了?岂不是浪费?为了提高性能,它会尝试下一条指令能不能先执行了?,如果可以,那么CPU就不会闲下来了。
因此,为了提高执行效率,编译器和CPU都会进行指令的重排序。 4.1.2 happens-before顾名思义,如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。 不用太纠结这个概念。这只是一个规范而已,本质上就是说这个规范实现的代码肯定是加了内存屏障的。 如下这些代码实现方式,就是符合happens-before规则的:
4.2 synchronizesynchronize不管是修饰方法还是代码块,都需要用到锁对象。而锁对象的锁是有状态的,它会升级也会降级。锁的是对象,而不是把代码块锁住了! 锁的状态(jdk1.6后):
4.2.1 对象头在jvm中,每个Object对象在内存中的布局有三部分组成:
举个例子: Object0=new Object(); o对象占多少内存? 假如是64位系统: 对象头+实例对象,也就是 8+4+0=12,不能被8整除,所以还需要加上padding 4. 因此,最终的o对象占用了16个字节。 请注意!!! markword就是用来存储锁信息的地方。 一共32/64位,多少位没有太大关系。我们只需要知道里面有什么即可。 4.2.2 锁的升级过程
讲了这么多,synchronize的底层到底是怎么实现的? 其实还是 4.3 volatilevolatile修饰变量后有两个作用: 1,内存可见性 线程间工作内存和主存实现了及时同步 2,防止指令重排 这对这个变量的操作被JMM加入内存屏障来保证指令不会乱序执行。 volatile到底是怎么解决指令重排的?? JVM层通过加入内存屏障,是一个逻辑实现,是jvm的要求规范而已,具体要看汇编语言。
四个逻辑。 具体 就是在volatile读/写的前后加入内存屏障,保证顺序执行。内存屏障前后的指令不能重排序! 汇编层面: 最终就是调用了 为什么这条指令能实现内存可见和禁止指令重排序?? 内存可见性: 该指令能够将当前处理器对应缓存内容刷新到内存,并且是其他处理器的缓存失效。 重排序: 该指令本身就是内存屏障,它前面的指令和后面的指令都不能重排序。 4.4 CAS和原子操作4.4.1 乐观锁和悲观锁
4.4.2 CASCAS(compare and swap): 比较并且交换。 目的: 在没有锁的状态下,可以保证多个线程对一个值的更新。 CAS实现思想:
举个例子: 假设i=0,对i做++操作。 CAS的过程是这样的:
流程图: - 实现的本质: AtomicInteger内部就是通过CAS的方式来保证线程安全的。 内部会调用UnSafe类的方法。
UnSafe类直接调用的是C++层的native方法
通过源码发现
如果是多个处理器则 进行lock。为什么? 多个CPU就会出现多线程同时执行,出现并发问题。 而CAS方法会直接通过汇编指令: 所以, 现在的问题变成 4.5 AQS(AbstractQueuedSynchronizer) 抽象队列同步器高并发编程的核心: AQS。 里面通过维护一个volatile int state变量和一个存储线程的队列(双向链表)来实现同步的。 它本身是一个抽象的类,定义了同步模板方法。具体逻辑需要子类去继承实现。 可通过构造方法传入是否是公平锁。
4.5.1 ReentrantLock和synchronize的区别?synchronize: 最终要通过用户态到内核态的切换,但是有锁的升级优化。悲观锁 ReentrantLock(jdk1.5后新增的锁): 基于AQS同步机制,其实内部还是通过CAS来获取锁,不用到内核态,轻量级。更加灵活。属于 乐观锁。 需要自己手动try catch,在finally中释放锁。 分享小编学习提升时,顺带从网上收集整理了一些 Android 开发相关的学习文档、面试题、Android 核心笔记等等文档,希望能帮助到大家学习提升,如有需要参考的可以直接去我 CodeChina地址:https://codechina.csdn.net/u012165769/Android-T3 访问查阅。 |
|
移动开发 最新文章 |
Vue3装载axios和element-ui |
android adb cmd |
【xcode】Xcode常用快捷键与技巧 |
Android开发中的线程池使用 |
Java 和 Android 的 Base64 |
Android 测试文字编码格式 |
微信小程序支付 |
安卓权限记录 |
知乎之自动养号 |
【Android Jetpack】DataStore |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 10:13:04- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |