Java多线程CAS

文章讨论了在多线程环境下,如何保证对共享资源如balance的线程安全性。通过举例不安全的Account实现,指出了不使用锁会出现的问题,如原子性、可见性问题。接着提出了两种解决方案:使用synchronized关键字实现加锁保护,以及利用AtomicInteger和CAS实现无锁保护。CAS基于乐观锁思想,适合线程竞争不激烈的情况,而synchronized则是悲观锁的体现。
摘要由CSDN通过智能技术生成

多线程下对共享资源的保护

有一个Account接口,一个抽象方法withdraw(int money) 取款方法,一个抽象方法getBalence()获取余额。现在需要保证对共享资源balance的线程安全性。

public interface Account{
	void withdraw(Integer money);
	Integer getBalance();
}

如果不使用锁会出现线程安全问题

class UnsafeAccount implements Account{
	private Integer balance;
	void withdraw(Integer money){
			balance -= money;
	}
}

书写测试方法

/**
     * 多线程对共享资源访问的测试类
     * @param accountMethod account的多种实现
     */
public static void demo(Account accountMethod){
        Account account = accountMethod;
        ArrayList<Thread> accountList= new ArrayList<>();
        CountDownLatch countDownLatch = new CountDownLatch(1000);
        for (int i = 0; i < 1000; i++) {
            accountList.add(new Thread(()->{
                account.withDraw(10);
                System.out.println("取了10元余额为" + account.getBalance() + ",还剩"+ countDownLatch.getCount() + "次");
                countDownLatch.countDown();
            }));
        }
        long nacos = System.nanoTime();
        for (int i = 0; i < 1000; i++) {
            accountList.get(i).start();
        }
        countDownLatch.await();
        long nacos1 = System.nanoTime();
        System.out.println("余额 = " + account.getBalance());
        System.out.println("消耗时间:" + (nacos1 - nacos)/1000_000 + "ms");
    }
  • 由于balance -= money这个指令不具有原子性,所以会出现线程安全问题。比如A线程读取到变量balance,此时B线程也读取到balance并且已经完成了balance-money操作,此时A线程再对变量balance进行运算时就会导致balance本来应该减了两次,实际上只减了一次。这是不满足原子性的体现。
  • 再比如线程A对balance变量进行修改后,balance还没写进主存中。此时B线程从主存中读取到了还没修改完成的balance变量进行操作也会导致数据不一致的结果。这是不满足可见性的体现。
  • 由于本例子中只有一行操作,所以有序性暂时可以保证。

加锁保护共享变量

  • 我们使用sychronized实现,只需要在withdraw方法加一个sychronized就可以了
  • 为了保证读操作的安全性,需要在getBalance()方法也加上sychronized来保证读取的时候不会有其他线程对balance进行修改,防止读到了脏数据。

无锁保护共享变量

主要使用到CAS方式来实现。

class SafeAccount implements Account{
        private AtomicInteger balance;
        public SafeAccount(int balance){
            this.balance = new AtomicInteger(balance);
        }
        @Override
        public void withDraw(Integer amount) {
            while (true){
                //获取余额最新值
                int prev = balance.get();
                balance.getAndAdd(5)
                int next = prev - amount;
                /**
                 * 比较prev,是否等于balance的值
                 * 如果相等,修改balance的值为next
                 * 如果不相等,进行重试
                 */
                if(balance.compareAndSet(prev,next)){
                    break;
                }
            }

        }
        public static void demo() throws InterruptedException {
            Account.demo(new SafeAccount(10000));
        }
        @Override
        public Integer getBalance() {
            return balance.get();
        }
    }

CAS和volatile可以实现无锁并发,适用于线程数少、多个cpu的场景。CAS的方法相比传统的加锁方法,虽然是用到了自旋,但是效率比加锁是要高的,因为线程是一直处于活跃状态。而加锁后线程会进入到Block状态,获取到锁后才处于就绪状态来抢夺cpu时间片,这种线程状态的切换开销是比较大的。

  • CAS是基于乐观锁的思想,sychronized是居于悲观锁的思想
  • CAS体现的是无锁并发、无阻塞并发。如果竞争激烈,重试的情况会频繁发生,反而效率会受到影响。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值