标签:10 Account 之无锁 并发 amount 线程 Integer balance public
一个小例子引入
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
interface Account {
Integer getBalance();
void withdraw(Integer amount);
static void demo(Account account) {
List<Thread> ts = new ArrayList<>();
long start = System.nanoTime();
for (int i = 0; i < 1000; i++) {
ts.add(new Thread(() -> {
account.withdraw(10);
}));
}
ts.forEach(Thread::start);
ts.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long end = System.nanoTime();
System.out.println("账户余额:" + account.getBalance() + "\ntime cost:" +
(end - start) / 1000 + "ms");
}
}
//线程不安全的版本,多线程执行取款操作会出现问题
class AccountUnsafe implements Account {
private Integer balance;
public AccountUnsafe(Integer balance) {
this.balance = balance;
}
@Override
public Integer getBalance() {
return balance;
}
@Override
public void withdraw(Integer amount) {
balance = balance - amount;
}
}
//用锁实现的线程安全的版本
class AccountSafe implements Account {
private Integer balance;
public AccountSafe(Integer balance) {
this.balance = balance;
}
@Override
public Integer getBalance() {
return balance;
}
@Override
public void withdraw(Integer amount) {
synchronized (this) {
balance = balance - amount;
}
}
}
//用cas实现的线程安全的版本,compare and set
class AccountSafe01 implements Account {
private AtomicInteger balance;
public AccountSafe01(Integer 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;
}
}
}
}
public class TestAccount {
public static void main(String[] args) {
// Account.demo(new AccountUnsafe(10000));
// Account.demo(new AccountSafe(10000));
Account.demo(new AccountSafe01(10000));
}
}
以上是一个模拟银行取款的测试,分别用synchronized和cas无锁机制实现了线程安全的操作。
cas和volatile
withdraw的cas方法实现:
public void withdraw(Integer amount) {
while(true) {
// 需要不断尝试,直到成功为止
while (true) {
// 比如拿到了旧值 1000
int prev = balance.get();
// 在这个基础上 1000-10 = 990
int next = prev - amount;
/*
compareAndSet 正是做这个检查,在 set 前,先比较 prev 与当前值
- 不一致了,next 作废,返回 false 表示失败
比如,别的线程已经做了减法,当前值已经被减成了 990
那么本线程的这次 990 就作废了,进入 while 下次循环重试
- 一致,以 next 设置为新值,返回 true 表示成功
*/
if (balance.compareAndSet(prev, next)) {
break;
}
}
}
}
最关键的操作就是compareAndSet操作,简称CAS,CAS底层有机制保证它一定是原子性的
volatile
获取共享变量时,为了保证共享变量的可见性,需要用volatile来修饰
它可以用来修饰成员变量和静态成员变量,可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,线程操作volatile变量都是直接操作主存。即另一个线程对volatile变量的修改,对另一个线程可见
volatile仅仅保证了共享变量的可见性,让其他线程能看到最新值,但并不能解决指令交错的问题
CAS必须借助volatile才能读取到共享变量的最新值来实现比较并交换的效果。
为什么无锁效率高?
无锁情况下,即使重试失败,线程也在告诉运行没有停歇,而synchronized会让线程在获得锁的时候发生上下文切换,进入阻塞。
CAS的特点
结合CAS和volatile可以实现无锁并发,适用于线程数少,多核CPU的场景下
CAS是基于乐观锁的思想,最乐观的估计,就算别的线程来修改了我的共享变量,我也可以识别读取并再修改一次
synchronized是基于悲观锁的思想,最悲观的估计,需要防着其他线程来修改共享变量,只有等我这个线程修改完了这个共享变量解锁之后,其他线程才能来修改
CAS体现的是无锁并发,无阻塞并发。但是如果竞争非常激烈,导致修改的重试频繁发生,反而效率会受到影响
标签:10,Account,之无锁,并发,amount,线程,Integer,balance,public 来源: https://www.cnblogs.com/wbstudy/p/16654952.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。