多线程和高并发(三)| Compare And Swap
系列文章目录
多线程和高并发(一) Thread
多线程和高并发(二) volatile关键字
多线程和高并发(三) Compare And Swap
多线程和高并发(四) 线程间通信
多线程和高并发(五) ThreadLocal & Lock
多线程和高并发(六) ReentrantLock
多线程和高并发(七) 线程池
文章目录
线程同步
CAS(Compare And Swap)
是由硬件实现的,CAS可以将读改写(read - modify - write)这类的操作转化为原子操作。
i++ 自增操作包括三个操作:
- 从内存中读取i的值
- 对i的值加1
- 把加1 的值之后的值保存到主内存
CAS原理:在数据更新到主内存时,在读取主内存的值,如果期望值和主内存值相同,则将修改值更新到主内存中
/**
* @author 奋斗的蜗牛_Z99
* @CopyRight (C) https://blog.csdn.net/mr_zhu_wenxing?spm=1001.2101.3001.5343
* @date 2021/5/6
* @description CAS模拟
*/
public class CAS extends Thread {
//volatile确保读取值从主内存读取(可见性)
private volatile static int value = 0;
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
getAndIncrement();
}
System.out.println(value);
}
//此方法需要保障同步
static synchronized boolean compareAndSwap(int expectValue, int newValue) {
if (value == expectValue) {
value = newValue;
return true;
} else
return false;
}
int getAndIncrement() {
int oldValue;
int newValue;
do {
oldValue = value;
newValue = oldValue + 1;
} while (!compareAndSwap(oldValue, newValue));
return newValue;
}
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
new CAS().start();
}
}
}
这种操作只满足非ABA修改,比如共享变量 count = 0
- A 线程对 count 值修改为 10
- B 线程对 count 值修改为 20
- C 线程对 count 值修改为 0
这就是CAS的A->B->A更新,如果需要规避ABA问题,可以为共享变量引入一个修订号(时间戳),每次更改修订号加一,ABA变量更新过程变量: [A,0] ->[B,1]->[A,2],AtomicStampedReference类就是基于这种思想。
原子变量类
原子变量类是基于CAS实现的,对共享变量进行read-modify-write 更新操作时,通过原子变量类可以保障操作的原子性和可见性,原子类有12个:
分组 | 原子变量类 |
---|---|
基本数据型 | AtomicInteger AtomicLong AtomicBoolean |
数组型 | AtomicIntegerArray AtomicLongArray AtomicReferenceArray |
字段更新器 | AtomicIntegerFieldUpdater AtomicLongFieldUpdater AtomicReferenceFieldUpdater |
引用型 | AtomicReference AtomicStampedReference AtomicMarkableReference |
详细使用略
线程间的通信
等待/通知机制
什么是等待通知机制
在单线程编程中,要执行的操作需要满足一定的条件才能执行,可以把这个操作放在 if语句块中在多线程编程中,可能 A 线程的条件没有满足只是暂时的, 稍后其他的线程 B 可能会更新条件使得 A 线程的条件得到满足。可以将A线程暂停,直到它的条件得到满足后再将 A 线程唤醒它的伪代码:
atomics{
//原子操作
while( 条件不成立 ){
等待
}
当前线程被唤醒条件满足后,继续执行下面的操作
}
等待通知机制的实现
等待:
- Object的wait()方法可以暂停执行,直到接到通知或中断为止
- 只能被锁对象调用
- 调用后会释放锁对象,进入WAITING状态
//在调用 wait()方法前获得对象的内部锁
synchronized( 锁对象 ){
while( 条件不成立 ){
//通过锁对象调用 wait()方法暂停线程,会释放锁对象
锁对象.wait();
}
//线程的条件满足了继续向下执行
}
通知唤醒:
- Object 类的notify()可以唤醒线程,只能被锁对象调用
- notify()随机唤醒一个线程
- 没有锁对象调用notify/waitd方法会抛出IllegalMonitorStateException
- 调用 notify()方法后,并不会立即释放锁对象,需要等当前同步代码块执行完后才会释放锁对象
synchronized( 锁对象 ){
//执行修改保护条件的代码
//唤醒其他线程
锁对象.notify();
}