记录并发相关知识点
内存模型
Java线程-工作内存-内存操作-主内存
每个Java线程都有自己对应的工作内存,工作内存线程私有,必须通过主内存进行数据交换。
内存间交互操作
主内存和工作内存之间有具体的交互协议,实现从主内存到交互内存的拷贝和同步。有lock,unlock,read,load,use,assign,store,write。规定了一系列的访问规则。
volatile关键字
- 可见性:一个线程对volatile修饰的变量修改之后,立刻对其它线程可见。但是,有些运算可能并非原子的,不能保证并发安全。使用情况需要满足:
- 运算结果并不依赖变量的当前值,或者能够确保只有单一线程能够修改变量的值。
- 变量不需要与其它的状态共同参与不变性约束。
- 禁止语义重排
- 相当于添加了内存屏障
内存模型特性
- 对于long和double类型需要注意JVM对64数据是否为原子的
- 原子性:synchronized关键字
- 可见性:volatile synchronized final关键字实现
- 有序性:volatile
happen-before原则
Java内存模型下有一些天然的先行发生关系:
- 程序次序规则
- 管程锁定规则
- volatile规则
- 线程启动规则
- 线程终止规则
- 线程中断规则
- 对象终结规则
- 传递性
Java线程实现
- 线程实现
- 内核级线程
- 一般通过轻量级进程包装内核线程,然后再使用
- 轻量级进程和线程之间1:1
- 由于需要在用户态和内核态之间切换,系统调用代价较高
- 用户线程
- 线程建立,同步,销毁,调度完全在用户态中完成。
- 进程和线程之比为1:N
- 用户线程加轻量级线程
- 混合使用,进程和线程之间N:M
- 内核级线程
- Java线程实现
- 1.2之前是用户线程
- 1.2之后是基于操作系统原生线程来实现的
- Java线程调度
- 协同式线程调度:线程执行完通知其它线程执行
- 好处是实现简单
- 坏处是线程执行时间不可控制,如果一个线程编写有问题,容易造成阻塞。
- 抢占式线程调度
- 每个线程由系统来分配执行时间。
- 协同式线程调度:线程执行完通知其它线程执行
- 线程状态转换
- 新建
- 运行:包括系统线程中的Running和Ready,有可能在等待CPU调度
- 无限期等待:需要等待被其它线程唤醒。
- 没有设置TimeOut的Object.wait()
- 没有设置TimeOut的Thread.join()
- LockSupport.park()
- 有限期等待:一定时间之后被系统自动唤醒
- Thread.sleep()
- 设置TimeOut的Object.wait()
- 设置TimeOut的Thread.join()
- LockSupport.parkNanos()
- LockSupport.parkUntil()
- 阻塞:等待获得排它锁,在等待另一个线程释放锁时发生。
- 结束
线程安全
- 定义
- 当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步,或者在调用代码代码不必作其他的协调,这个类的行为仍然是正确的,那么称这个类是线程安全的。
- 特征
- 代码本身封装了所有必要的正确性保障手段
- Java操作共享数据分类(线程安全程度由强到弱)
- 不可变
- String,Long,Double,BigInteger等为不可变对象
- AtomicInteger和AtomiLong则为可变类
- 绝对线程安全
- java.util.Vector,方法被synchronized修
- 相对线程安全:特定的执行顺序需要同步
- Vector,HashTable,Collections
- 线程兼容:对象本身是线程非线程安全的,但是可以使用同步手段让对象在并发情况下安全使用。
- 线程对立:无法同步的代码。
- 不可变
- 线程安全的实现
- 互斥同步
- 可以使用临界区,互斥量,和信号量、
- synchronized关键字,Java里面会在同步块前后加上monitorenter和monitorexist关键字,重量级操作
- ReentrantLock和synchronized很相似,写法上不太同
- 等待可中断
- 可实现公平锁
- 锁绑定多个条件
- 非阻塞同步
- 基于冲突检测和乐观并发策略,先操作,有冲突,再补偿,需要硬件指令支持。
- Test-and-set,Fetch-and-Increment,Swap,Compare-and-Swap(CAS),Load-Linked/Store-Conditional
- 无同步方案
- 可重入代码
- 线程本地存储
- 互斥同步
- 锁优化(1.5到1.6)
- 适应性自旋
- 自旋锁,多处理器上,一个线程请求资源被上锁,可以占用CPU自旋(忙循环),避免线程切换。
- 适应性自旋意味着自旋时间不是固定的,由前一次在同一个锁上的自旋时间及锁拥有者的状态来决定。
- 锁消除
- 对不可能存在数据竞争的代码进行锁消除
- 锁粗化
- 如果虚拟机探测到一连串的对象都涉及到一个锁,可以将锁扩展到这个操作序列的外部。
- 轻量级锁
- 传统锁使用互斥量来实现
- 轻量级锁通过CAS,对象头信息中的锁标志位实现。如果存在竞争关系,轻量级锁会膨胀为重量级锁。
- 偏向锁
- 消除数据在无竞争情况下的同步原语。
- 适应性自旋
MySQL锁
锁是计算机协调多个进程或线程并发访问某一资源的机制。锁保证数据并发访问的一致性、有效性;锁冲突也是影响数据库并发访问性能的一个重要因素。锁是Mysql在服务器层和存储引擎层的的并发控制。
加锁是消耗资源的,锁的各种操作,包括获得锁、检测锁是否是否已解除、释放锁等。
设计模式
Semphore
限制线程并发的数量
Exchanger
在两个线程之间传输数据