CAS问题

CAS问题

1、共享资源的访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public interface Account {

///查询钱
Integer getBalance();

//取钱
void withDraw(Integer amount);

//设置一个静态方法,设置1000个线程 每个线程取钱
///在控制台打印余额和耗时
static void demo(Account account){
List<Thread> ts = new ArrayList<>();

for (int i = 0; i < 1000; i++) {
ts.add(new Thread(() -> {
account.withDraw(10);
}));
}

long start = System.nanoTime();
ts.forEach(Thread::start);
ts.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});

long end = System.nanoTime();
System.out.println("余额还有:" + account.getBalance() + " cost:" + (end-start)/1000_100 + " ms");
}
}

1、加锁:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//设计一个类,重写接口
class AccountSafe implements Account{
//余额
private Integer balance;
//构造函数
public AccountSafe(Integer balance) {
this.balance = balance;
}
//
@Override
public Integer getBalance() {
//加锁
synchronized (this){
return this.balance;
}
}
//取钱
@Override
public void withDraw(Integer amount) {
//加锁
synchronized (this){
this.balance -= amount;
}
}
}

public class DemoSynchronized {

public static void main(String[] args) {
Account accountSafe = new AccountSafe(10000);
Account.demo(accountSafe);
}
}

加锁保证线程安全

2、 CAS(无锁)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class AccountCAS implements Account{

//原子类
private AtomicInteger balance;

public AccountCAS(int balance) {
this.balance = new AtomicInteger(balance);
}

@Override
public Integer getBalance() {
return balance.get();
}

@Override
public void withDraw(Integer amount) {
while(true){
//获得余额的最新值
int prev = balance.get();
//要修改的值
int next = prev - amount;

//真正修改balance
/**
* balance.compareAndSet(prev,next)
* 参数1:最新值
* 参数2:修改后的结果
*/
if (balance.compareAndSet(prev, next) == true){
break;
}

}
}
}
public class DemoCAS {
public static void main(String[] args) {
Account accountCAS = new AccountCAS(10000);
Account.demo(accountCAS);

}
}

无锁保证线程安全

2、CAS工作原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void withDraw(Integer amount) {
while(true){
//获得余额的最新值
int prev = balance.get();
//要修改的值
int next = prev - amount;

//真正修改balance
/**
* balance.compareAndSet(prev,next)
* 参数1:最新值
* 参数2:修改后的结果
*/
if (balance.compareAndSet(prev, next) == true){
break;
}

}
}

通过CAS无锁的方式来保证线程安全。其中的关键是CompareAndSet,简称CAS(也可称为Compare And Swap),它是一个由底层指令实现的原子操作。

1
2
3
4
5
原理:
我们得到余额,计算出要修改的值。
然后我们在判断余额的最新值和余额是否一致。这一步是原子操作
如果是,返回true,并将余额改为要修改的值
如果不是,返回false,继续循环
CAS的工作原理:

CAS原理

CAS的底层实现

其实CAS的底层是lock cmpxchg指令(X86架构),在单核CPU和多核CPU下都能够保证CAS的原子性

AtomicInteger原子类
1
2
3
4
5
//查看AtomicInteger 源码
//AtomicInteger的值是一个int类型的变量 ,并且由volatile修饰
public class AtomicInteger extends Number implements java.io.Serializable {
private volatile int value;
}

==CAS必须要volatile的支持才能实现==

3、理解CAS

CAS可以无锁保证线程的安全性,但适用于线程数少,多核CPU的场景下。

  1. CAS是基于乐观锁的思想。最乐观的估计,不怕被其他线程修改,如果被改变了,只要重新修改值。
  2. synchronized是基于悲观锁的思想:最悲观的估计,要防止其他线程来改变变量。
  3. CAS体现的是无锁并发,无阻塞并发
    • 没有使用synchronized,所以线程不会陷入阻塞
    • 但如果竞争激烈,重试必然频繁发生,效率会受到影响

CAS问题
https://johnjoyjzw.github.io/2021/02/01/CAS问题/
Author
John Joy
Posted on
February 1, 2021
Licensed under