硬件同步原语
硬件同步原语(Atomic Hardware Primitives)是由计算机硬件提供的一组原子操作,我们比较常用的原语主要是 CAS 和 FAA 这两种。
原语:原子操作,只要当前线程执行完毕之后,才会切换下一个线程执行。
CAS
CAS(CompareAndSwap),即先比较,再交换。
<< atomic >>
// p: 要修改的变量的指针。
// old: 旧值
// new: 新值
function cas(p : pointer to int, old : int, new : int) returns bool {
if *p ≠ old {
return false
}
*p ← new
return true
}
机制:当在该线程的工作内存中的旧值,与要修改变量的指针的值(也就是在主内存)相同时,表明此时没有线程修改它。就会把要修改的值修改为新值。
FAA
Fetch And Add
<< atomic >>
function faa(p : pointer to int, inc : int) returns int {
int value <- *location
*p <- value + inc
return value
}
语义:先获取变量 p 当前的值 value,然后给变量 p 增加 inc,最后返回变量 p 之前的值 value
实现
Java Synchronized、ReentrantLock
package com.rpq.leetcode.lock;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* 用锁实现异步并发实现转账
* @author renpeiqian
* @date 2021/7/17 10:29
* @since
*/
public class LockTest {
//定义初始余额为0
private static int balance=0;
public static void main(String[] args) {
//定义循环次数
int count=1000000;
//ReentrantLock底层就是cas
ReentrantLock lock =new ReentrantLock();
long start = System.currentTimeMillis();
for (int i = 0; i <count ; i++) {
//CompletableFuture.runAsync(()-> transferSynchronized(1));
CompletableFuture.runAsync(()-> transfer(1,lock));
}
while (balance < count) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println(end-start);
System.out.println(balance);
}
/**
* @Description 转账业务
* @Param [amount, lock]
* @return void
*/
private static void transfer(int amount,ReentrantLock lock){
//开锁
// 使用阻塞锁,长搭配try finally 块来保证锁的释放。并且lock.lock 方法与try之间不能发生任何异常
lock.lock();
try{
//转账
balance += amount;
}finally {
lock.unlock();
}
}
/**
* @Description 使用synchronized同步锁实现转帐
* @Param [amount]
* @return void
*/
private static void transferSynchronized(int amount){
synchronized (LockTest.class){
balance += amount;
}
}
}
CAS、FAA
package com.rpq.leetcode.lock;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author renpeiqian
* 使用硬件原语cas、Faa实现转账业务
* @date 2021/7/17 11:23
* @since
*/
public class CasFaaLock {
//使用原子整形类创建初始余额为0
private static AtomicInteger balance =new AtomicInteger(0);
public static void main(String[] args) {
int count=1000000;
long start = System.currentTimeMillis();
for (int i = 0; i <count ; i++) {
CompletableFuture.runAsync(()-> casTransfer(1));
}
while(balance.get()<count){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println(end-start);
System.out.println(balance);
}
/**
* @Description cas(compare and swap )
* @Param [amount] 转账数目
* @return void
*/
private static void casTransfer(int amount){
//死循环 相当于自旋
for (;;){
//获取旧值
int old = balance.get();
int newBalance = old + amount;
//如果当前线程工作内存中值与主内存值保持一直,可以修改该值
if (balance.compareAndSet(old,newBalance)){
//修改成功,终止循环
break;
}
//修改失败,说明其他线程已经修改过,自旋重新尝试修改
}
}
/**
* @Description
* 使用FAA(Fetch And Add 即取到值进行自增) 实现转账
* 对于FAA,通过查找资料,jdk1.8在调用sun.misc.Unsafe#getAndAddInt方法时,
* 会根据系统底层是否支持FAA,来决定是使用FAA还是CAS。
* @Param [amount]
* @return void
*/
private static void faaTransfer(int amount){
//获取值然后在增加
balance.getAndAdd(amount);
}
}