以加锁的方式保护共享资源现成安全
public class AccountDemo {
public static void main(String[] args) {
Account account = new AccountUnsafe(10000);
Account.demo(account);
}
}
class AccountUnsafe implements Account {
private Integer balance;
public AccountUnsafe(Integer balance) {
this.balance = balance;
}
//对共享变量的读操作加锁
@Override
public Integer getBalance() {
synchronized (this) {
return this.balance;
}
}
//给共享变量的写操作加锁
@Override
public void withDraw(int amont) {
synchronized (this) {
this.balance -= amont;
}
}
}
interface Account {
//获取余额
Integer getBalance();
//取款
void withDraw(int amont);
static void demo(Account account) {
//定义一个线程集合
List<Thread> ts = new ArrayList<>();
long start = System.nanoTime();
//创建一千个线程,每个线程取出10元
for (int i = 0; i < 1000; i++) {
ts.add(new Thread(() -> account.withDraw(10)));
}
//启动每一个线程
ts.forEach( e -> e.start());
//等所有线程执行完毕
ts.forEach( e -> {
try {
e.join();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
});
long duration = System.nanoTime() - start;
System.out.println("花费时长:"+duration);
System.out.println("余额:"+ account.getBalance());
}
}
以无锁,不加锁保证共享资源的线程安全性,使用AtomicInteger类
class AccountUnsafe implements Account {
//使用AtomicInteger
private AtomicInteger balance;
public AccountUnsafe(Integer balance) {
this.balance = new AtomicInteger(balance);
}
@Override
public Integer getBalance() {
//AtomicInteger的get方法没有加锁
return balance.get();
}
@Override
public void withDraw(int amont) {
while(true) {
//获取修改前的余额
int oldVal = this.balance.get();
//得到修改后的余额
int newVal = oldVal - amont;
//修改为新余额之前,要判断是否跟旧余额一致
//AtomicInteger的compareAndSet方法没有枷锁
if(balance.compareAndSet(oldVal, newVal)) {
break;
}
//否则进入下一次循环
}
}
}
使用AtomicInteger提供的方法,进一步简化代码如下:
@Override
public void withDraw(int amont) {
balance.addAndGet(-1 * amont);
//这里等价于下面注释的代码内容
/*
while(true) {
//获取修改前的余额
int oldVal = this.balance.get();
//得到修改后的余额
int newVal = oldVal - amont;
//修改为新余额之前,要判断是否跟旧余额一致
//AtomicInteger的compareAndSet方法没有枷锁
if(balance.compareAndSet(oldVal, newVal)) {
break;
}
//否则进入下一次循环
}
*/
}
compareAndSet方法简称CAS,Compare And Swap比较并交换,它必须是原子操作。
compareAndSet方法是原子的,不可分割的。是在cpu的指令集中实现其原子性。
CAS必须得到volatile的支持,因为比较并交换,每次都需要读取共享变量最新的值来实现比较。
CAS的效率比synchronized的效率更高。
无锁的情况下,即使重试失败,线程始终在高速运行,没有停歇。而synchronized会让线程在没有获得锁的时候,发生上下文切换,进入阻塞。
CAS适合的场景,线程数较少,多核cpu的场景。
CAS的线程数不要多于cpu的核心数。
CAS是基于乐观锁的实现,最乐观的估计,不怕别的线程来修改共享变量,就算改了也没有关系,我吃点亏再重试。
synchronized是基于悲观锁的实现,防着其他线程来修改共享变量,我上了锁你们都别改,我改完解开锁,你们才有机会。
CAS体现的无锁并发,无阻塞并发。
1.因为没有使用synchronized,所以线程不会陷入阻塞,这是效率提升的因素之一。
2.如果竞争激烈,可以想到重试必然频繁发生,反而效率受到影响。