在Java中,CAS代表Compare and Swap(比较并交换),是一种用于实现多线程并发操作的原子操作。
比如,拿寄存器A中的值和内存value的值进行比较,如果这两个值不相同,则什么操作都不做。但如果寄存器中A的值和内存中value的值相同,怎么寄存器B中的值和内存中value的值进行交换。这里并不关心交换之后B的值,更关心的是交换之后value的值,此处的交换相当于是把B赋值给了value。注意,这一系列操作是由CPU的一条指令完成的,这个操作是一个原子操作,也就是一个线程安全的操作,效率也非常高。
自增操作
CAS的使用操作的使用场景,典型的就是多线程环境下对变量自增。
下面这个是一个伪代码:
class MyAtomicInteger {
private int value;
/**
这个方法相当于对变量自增,也就是++操作
*/
public int getAndIncrement() {
// 假设oldValue是是寄存器A的值
int oldValue = value;
//CAS(内存中的值,寄存器A中的值,寄存器B中的值)
while (CAS(value,oldValue,oldValue+1) != true) {
oldValue = value;
}
return oldValue;
}
}
实现自旋锁
自选锁是纯用户态的轻量级锁,当发现锁被其它线程持有的时候,另外的线程不会挂起等待,而是会反复询问,看单前的锁是否被释放了。
伪代码实现:
class SpinLock {
private Thread owner;
public void lock() {
// 通过CAS看当前锁是否被某个线程持有
// 如果这个锁已经被别的线程持有,那么就自旋等待
// 如果这个锁没有被别的线程持有,那么就把owner设为当前尝试加锁的线程
while (!CAS(this.owner,null,Thread.currentThread())) {
}
}
public void unlock() {
this.owner = null;
}
}
自旋锁是非常消耗CPU资源的,但是它可以锁被释放时立即获得锁,适用于锁竞争比较小的场景。
举个例子:
我的账户余额里有1000块钱,我从ATM中取出100块,取的时候ATM卡了一下,我按了两下取钱。
这时候有两个线程,线程1和线程2都尝试进行 -100操作
线程1 获取到当前的账户余额为1000,线程2 也获取到当前的账户余额为 1000
线程1执行 -100操作,对比当前账户余额是一致的取钱成功,此时余额变成900,此时线程2就会在阻塞状态。
线程1释放锁后,线程2也尝试进行-100操作发现账户余额和当前自己读取的不一致就不会进行取钱操作。
此时相当于线程1修改成功,线程2修改失败(虽然卡了一下,出现了两个线程,但是由于CAS操作机制,保证了最终的结果是对的)
若果是极端情况:
线程1 获取到当前的账户余额为1000,线程2 也获取到当前的账户余额为 1000
线程1执行 -100操作,对比当前账户余额是一致的取钱成功,如果在取钱成功之后立马我的朋友又给我打了100块钱,接着线程2又执行 -100操作(此处不区分余额是否改变)
线程2发现此时的账户余额和刚刚读取的余额一致,于是就又扣了一次钱。
这就是CAS的ABA所引用起的问题。
CAS的ABA问题可以通过引入,版本号机制来解决。
只要有一个记录,能够记录内存中数据的变化,就可以解决ABA问题了,另外创建一个内存变量,保存账户余额的修改次数(这个版本号只增不减),或者是上次的修改时间(也是只增不减)。通过这个办法救可以解决ABA问题。
此时的修改操作,就不是把账户余额读取到寄存器A中了,比较的时候也不是比较账户余额,而是比较版本号/上次修改时间。
定时器是开发中非常重要的一个组件,用来定时执行一些指定任务。在Java标准库中提供了一个类Timer
来执行定时任务,该类有一个核心方法schedule
来添加任务。
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("5秒后执行的定时任务");
}
},5000);
}
模拟实现Timer
类:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
/**
* 模拟实现定时器Timer
*/
public class MyTimer {
private BlockingQueue<MyTimerTask> tasks;
private Object lock;
/**
* 这个类用来描述一个定时器任务
*/
static class MyTimerTask implements Comparable<MyTimerTask> {
private Runnable task;
private long time;
/**
*
* @param task 任务
* @param time 多长时间之后执行该任务
*/
public MyTimerTask(Runnable task, long time) {
this.task = task;
this.time = time+System.currentTimeMillis();
}
@Override
public int compareTo(MyTimerTask o) {
return (int)(this.time-o.time);
}
}
public MyTimer() {
this.tasks = new PriorityBlockingQueue<>();
this.lock = new Object();
// 起一个线程不断扫优先队列中的任务
Thread t = new Thread(()->{
while (true) {
try {
MyTimerTask timerTask = this.tasks.take();
synchronized (lock) {
long time = System.currentTimeMillis();
if (time >= timerTask.time) {
// 时间到了就执行任务
timerTask.task.run();
} else {
// 没有到之间就重新入队列,并阻塞一会
this.tasks.put(timerTask);
lock.wait(timerTask.time-time);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
// 防止InterruptedException异常
break;
}
}
});
t.start();
}
public void schedule(Runnable task, long time) {
MyTimerTask timerTask = new MyTimerTask(task,time);
try {
this.tasks.put(timerTask);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
// 每次添加任务后唤醒线程
lock.notify();
}
}
}