Java 无锁

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的说法),它必须是原子操作。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4K3iXN45-1607683516916)(C:/Users/lenovo/AppData/Roaming/Typora/typora-user-images/image-20201205200433233.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mOTf5SYA-1607683516928)(C:/Users/lenovo/AppData/Roaming/Typora/typora-user-images/image-20201205200600989.png)]

3、CAS 和 Volatile的关系

获取共享变量时,为了保证改变量的可见性,需要使用volatile来修饰。

它可以用来修饰成员变量和静态成员变量,他可以避免线程从自己的工作缓存中查找变量的值,必须从主存中获取它的值,线程操作volatile变量都是直接操作主存。即一个线程对volatile变量的修改,对另一个线程可见。

注意

volatile仅仅保证了共享变量的可见性,让其他线程能够看到最新值,但不能解决指令交错的问题(不保证原子性)

CAS必须借助volatile才能读取到共享变量的最新值来实现【比较并交换】得效果。

4、为什么无锁效率高?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n7Lmtypg-1607683516930)(C:/Users/lenovo/AppData/Roaming/Typora/typora-user-images/image-20201205202740393.png)]

一般来说:当线程数小于CPU数时,无锁效率比较高,但是当线程数大于CPU数时,就不一定了。

5、CAS的特点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vzkmrwaz-1607683516936)(C:/Users/lenovo/AppData/Roaming/Typora/typora-user-images/image-20201205203159719.png)]

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值