一、什么是CAS
CAS(Compare And Swap,比较并交换),通常指的是这样一种原子操作:针对一个变量,首先比较它的内存值与某个期望值是否相同,如果相同,就给它赋一个新值。
CAS实现过程如下图:
1、一个初始值变量V,值为5;一开始先读取V实际内存中的值赋值给E 2、比如我们需要给最原始的V+1操作,那么此时用E+1来进行操作(这是防止V在其他线程已经被改变),这样完成了U=E+1的操作 3、判断E和V的值是否一致,如果一致则证明在以上操作过程中V没有被其他线程改变则将U的值赋值给V,如果不一致那V就被其他改变了,这样给U的+1操作就不成立,返回当前的V。
CAS 的逻辑用伪代码描述如下:
if (value == expectedValue) {
value = newValue;
以上伪代码描述了一个由比较和赋值两阶段组成的复合操作,CAS 可以看作是它们合并后的整体——一个不可分割的原子操作,并且其原子性是直接在硬件层面得到保障的。
CAS可以看做是乐观锁(对比数据库的悲观、乐观锁)的一种实现方式,Java原子类中的递增操作就通过CAS自旋实现的。
CAS是一种无锁算法,在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步。
1-2、CAS应用
在java并发线程中,我们可以使用以下方式进行加锁处理:\
1、synchronize 在并发竞争比较激烈的时候不推荐使用,会让未获得锁的线程阻塞,因为会切换到内核态,进行park,这样就会有性能问题 2、使用ReentrantLock lock.lock(加锁) lock.unlock(解锁),需要注意的是unlock需要放到finally中,防止代码块异常,抛出后锁一直存在。 3、CAS也可以实现线程锁机制。
在 Java 中,CAS 操作是由 Unsafe 类提供支持的,该类定义了三种针对不同类型变量的 CAS 操作,如图
它们都是 native 方法,由 Java 虚拟机提供具体实现,这意味着不同的 Java 虚拟机对它们的实现可能会略有不同。
以 compareAndSwapInt 为例,Unsafe 的 compareAndSwapInt 方法接收 4 个参数,分别是:对象实例、内存偏移量、字段期望值、字段新值。该方法会针对指定对象实例中的相应偏移量的字段执行 CAS 操作。
1-3、并发锁的实现
1-3-1、使用synchronized
我们都知道用synchronized可以实现锁机制,但是在并发量大的时候,会**锁竞争的现象,这样就会涉及到用户态到内核态的切换**,这是极其耗费性能的,因此建议在并发量不大的情况下,可以使用synchronized进行加锁。并发量大的时候,不建议使用
public class ThreadLockSynchronized {
private volatile static int count = 0;
static Object object = "";
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (object) {
for (int j = 0; j < 10000; j++) {
count++;
}
}
}
});
thread.start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("count:" + count);
}
}
以上代码测试结果如下:
1-3-2、使用ReentrantLock
使用ReentrantLock的时候一定要注意,要将unlock()放到finally代码块中,防止业务代码异常,无法释放锁
public class ThreadLockReentrantLock {
private volatile static int count = 0;
static ReentrantLock reentrantLock=new ReentrantLock();
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
//使用reentrantLock.lock();进行加锁操作
reentrantLock.lock();
try {
for (int j = 0; j < 10000; j++) {
count++;
}
} finally {
//reentrantLock.unlock();一定要放到finally中,防止业务代码异常,导致锁不释放
reentrantLock.unlock();