其他分享
首页 > 其他分享> > 并发学习记录10:共享模型之无锁

并发学习记录10:共享模型之无锁

作者:互联网

一个小例子引入

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