Android笔记(十七)Android多线程问题
参考 https://mp.weixin.qq.com/s/gBZcogHizremZoJRvd8fHQ
线程的几个方法
- run()。非静态
- start()。非静态
- join()。非静态。如果线程 A 调用了线程 B 的 join() 方法,那线程 A 会进入等待状态,直到线程 B 运行结束。
- currentThread()。静态
- yield()。静态。降低当前线程优先级,调用该方法就像是是对线程调度器说:“如果其他线程要处理器资源,那就给它们,否则我继续用”。
- sleep()。静态
线程的生命周期
JVM内存模型
JAVA运行时的数据区域可以包括:
方法区。方法区存放类的信息(包括类的字节码,类的结构)、常量、静态变量等。字符串常量池就是在方法区中。
堆区。主要存放数组和对象,包括持久代,年老代,初生代等
虚拟机栈。线程独占。存放局部变量表(基础数据和引用)
本地方法栈。线程独占。存放native相关
程序计数器(线程寄存器)。线程独占。保存线程执行指令
JAVA内存模型
这幅图是区分于JVM内存模式。后者是从功能上区分内存模型,前者是从架构考虑,高速cache的存在是为了加速数据读写,而不同线程之间对内存的读写就涉及到了多线程并发工作的问题,从而产生了竞态这一说法。
线程安全性问题(竞态)
- 原子性。操作不可分割。其他线程不会参与
- 可见性。某一线程改变共享变量后需要同步到其他线程
- 有序性。保证操作的有序性。重排序(Reordering)处理器和编译器是对代码做的一种优化,它可以在不影响单线程程序正确性的情况下提升程序的性能,但是它会对多线程程序的正确性产生影响,导致线程安全问题。
实现线程安全(锁)
要实现线程安全就需要保证上面的三点。常见的实现线程安全的办法是使用锁和原子类型,而锁可分为内部锁(synchronized)、显式锁、读写锁、轻量级锁(volatile)四种
- 内部锁(synchronized)。同步方法或者代码块,锁住特定类和特定对象。因为使用 synchronized 实现的线程同步是通过监视器(monitor)来实现的,所以内部锁也叫监视器锁。不需要手动释放锁如(unlock),可以自动解锁。非公平锁
- 显示锁,实现了Lock接口。可重入,锁多次;需要手动获取和释放,允许调整锁策略成功公平/非公平锁。
- 读写锁。
- volatile 关键字
-
读线程执行的加载屏障和写线程执行的存储屏障配合在一起,能让写线程对 volatile 变量的写操作对读线程可见,从而保证了可见性;volatile 能禁止指令重排序,也就是使用 volatile 能保证操作的有序性;在原子性方面,对于 long/double 型变量,volatile 能保证读写操作的原子型。
对于非 long/double 型变量,volatile 只能保证写操作的原子性。
死锁 4 个必要条件
- 互斥
- 占有且等待
- 不可抢占
- 循环等待