1、共享资源——无锁实现
package com.sharing_model.no_lock;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 下面代码有什么问题? 很明显的发现是线程不安全的
* 解决方式:
* 1、加锁(synchronized,reentrantLock)
* 2、无锁(CAS)
*/
public class FoundProblem {
public static void main(String[] args) {
//线程不安全
// Account account = new AccountUnsafe(10000);
// Account.demo(account);、
//无锁(CAS)解决方式
Account accountCas = new AccountCas(10000);
Account.demo(accountCas);
}
}
//无锁实现
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;
//真正修改
if (balance.compareAndSet(prev, next)) {
break;
}
}
}
}
class AccountUnsafe implements Account {
private Integer balance;
@Override
public Integer getBalance() {
return this.balance;
}
public AccountUnsafe(Integer balance) {
this.balance = balance;
}
@Override
public void withdraw(Integer amount) {
this.balance -= amount;
}
}
interface Account {
//获取余额
Integer getBalance();
//取款
void withdraw(Integer account);
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());
}
}
2、CAS的工作方式
前面看到的AtomicInteger的解决方式,内部没有用锁来保护共享变量的线程安全,那么它是如何实现的?
public void withdraw {
while(true) {
int prew = balance.get();
int next = prev - amount;
//比较并设置值
if (balance.compareAndSet(prev, next) {
break;
})
}
}
其中关键的就是compareAndSet,它的简称就是CAS(也有Compare And Swap的说法),它必须是原子操作。
3、CAS 和 Volatile的关系
获取共享变量时,为了保证改变量的可见性,需要使用volatile来修饰。
它可以用来修饰成员变量和静态成员变量,他可以避免线程从自己的工作缓存中查找变量的值,必须从主存中获取它的值,线程操作volatile变量都是直接操作主存。即一个线程对volatile变量的修改,对另一个线程可见。
注意
volatile仅仅保证了共享变量的可见性,让其他线程能够看到最新值,但不能解决指令交错的问题(不保证原子性)
CAS必须借助volatile才能读取到共享变量的最新值来实现【比较并交换】得效果。
4、为什么无锁效率高?
一般来说:当线程数小于CPU数时,无锁效率比较高,但是当线程数大于CPU数时,就不一定了。