一文解决JAVA并发安全问题

项目场景:

并发的应用场景,在开发过程会经常遇到。 例如:服务应用启动后,需要简单统计接口的总访问量;实时更新订单状态,成交总额。


问题描述:

比如统计接口访问次数,如下的实现,在并发访问下,统计是不准确的 。

		private int viewCount = 0;
        private void addViewCount(){
        	viewCount++;
    	}
复制代码

比如A、B两个线程同时访问,各自从JVM主存中加载变量viewCount到线程内存里viewCount的值都是0,各自+1,更新会JVM主存的也是1。实际A、B执行完毕后,JVM的值应该是2才对。


解决方案:

在这里插入图片描述 并发问题解决,实际有2种方式:有锁、无锁。 有锁的就是关键字synchronized,以及可重入锁ReentrantLock。 无锁的,就是局部变量、不可变对象、CAS原子类、ThreadLocal,共四种。


具体解决方案分析:

一.无锁方式

1.局部变量

    /**
     * 局部变量,多线程更新count的时候,各自在线程内存中创建i变量。
     */
    public void localParam(){
        int count = 0;
        /*本次处理业务,统计*/
        count++;
        System.out.println(count);
    }
复制代码

2.不可变对象

车辆位置实时更新,传统的setY,setY,在并发过程会出差错。定义一个final localtion类,并且构造函数直接初始化x,y。

/**
 * 车辆位置经纬度值
 */
public final class Location {
    private final double x;
    private final double y;

    public Location(double x, double y) {
        this.x = x;
        this.y = y;
    }
}
复制代码

同时定义一个traker类,来存储多个车辆的位置信息,更新的时候直接用新的location位置类,去update ConcurrentHashMap。

/**
 * 不可变类,实现并发更新安全
 * 通过每次更新位置,直接初始化一个全新的location,然后set到车辆位置map中
 */
public class CarLocationTracker {
    /**
     * 车辆编码对应车辆位置信息map
     * ConcurrentHashMap,是利用CAS+synchronized来保证并发安全
     */
    private Map<String, Location> locationMap = new ConcurrentHashMap<>();

    /**
     * 更新车辆位置
     *
     * @param carCode     车辆编码
     * @param newLocation 车辆新位置
     */
    public void updateLocation(String carCode, Location newLocation) {
        locationMap.put(carCode, newLocation);
    }

    /**
     * 获取车辆位置
     *
     * @param carCode 车辆斌吗
     * @return
     */
    public Location getLocation(String carCode) {
        return locationMap.get(carCode);
    }
}
复制代码

3.ThreadLocal

ThreadLocal 变量,线程局部变量.同一个ThreadLocal所包含的对象,在不同线程中有不同的副本。

	private static final ThreadLocal threadCount= new ThreadLocal();
	/**
     * ThreadLocal 变量,线程局部变量
     */
    public void threadLocalParam(){
        Integer count = (Integer) threadCount.get();
        /*本次处理业务,统计*/
        count++;
        System.out.println(count);
    }
复制代码

4.CAS原子类

CAS 机制,三个基本操作:内存地址V,旧的预期值A,要修改的新值B,只有当内存地址V所对应的值与旧的预期值A相等,才会将内存地址V对应的值更新为B。

    private AtomicInteger counter = new AtomicInteger(0);
	/**
     * cas 原子类,是个乐观锁,并发性能很高。通过compare and swap比较并置换的原子性设计,read 从jvm主存中读取旧值oldV,
     * 更新的时候,先比较oldV与主存中的v是否相等,相等就把newV更新替换v;如果不相等,继续while循环,从主存读取'新的'旧值oldV。
     *
     * 底层是c++实现,保证三个步骤执行在硬件级别,是原子性,要么三个一起执行成功,又不继续循环直到成功。
     */
    public void atomicAdd(){
        //比如接口访问总次数统计
        System.out.println(counter.incrementAndGet());
    }
复制代码

二.有锁方式

1. 关键字synchronized

	//访问统计
    private int viewCount = 0;
      public synchronized void syncAdd(){
        addViewCount();
    }
    private void addViewCount(){
        viewCount++;
    }
复制代码

2. 可重入锁ReentrantLock

	//悲观锁
    private ReentrantLock lock = new ReentrantLock();
	//访问统计
    private int viewCount = 0;
    private void addViewCount(){
        viewCount++;
    }
    /**
     * 通过执行方法前,加锁;执行完毕主动释放锁保证int++ 并发安全
     */
    public void lockAdd(){
        lock.lock();
        try {
            addViewCount();
        } finally {
            lock.unlock();
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值