设为首页 加入收藏

TOP

quarkus依赖注入之九:bean读写锁(一)
2023-08-26 21:11:24 】 浏览:71
Tags:quarkus 赖注入 bean

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

本篇概览

  • 本篇是《quarkus依赖注入》的第九篇,目标是在轻松的气氛中学习一个小技能:bean锁
  • quarkus的bean锁本身很简单:用两个注解修饰bean和方法即可,但涉及到多线程同步问题,欣宸愿意花更多篇幅与各位Java程序员一起畅谈多线程,聊个痛快,本篇由以下内容组成
  1. 关于多线程同步问题
  2. 代码复现多线程同步问题
  3. quarkus的bean读写锁

关于读写锁

  • java的并发包中有读写锁ReadWriteLock:在多线程场景中,如果某个对象处于改变状态,可以用写锁加锁,这样所有做读操作对象的线程,在获取读锁时就会block住,直到写锁释放
  • 为了演示bean锁的效果,咱们先来看一个经典的多线程同步问题,如下图,余额100,充值10块,扣费5块,正常情况下最终余额应该是105,但如果充值和扣费是在两个线程同时进行,而且各算各的,再分别用自己的计算结果去覆盖余额,最终会导致计算不准确
流程图 (2)

代码复现多线程同步问题

  • 咱们用代码来复现上图中的问题,AccountBalanceService是个账号服务类,其成员变量accountBalance表示余额,另外有三个方法,功能分别是:
  1. get:返回余额,相当于查询余额服务
  2. deposit:充值,入参是充值金额,方法内将余额放入临时变量,然后等待100毫秒模拟耗时操作,再将临时变量与入参的和写入成员变量accountBalance
  3. deduct:扣费,入参是扣费金额,方法内将余额放入临时变量,然后等待100毫秒模拟耗时操作,再将临时变量与入参的差写入成员变量accountBalance
  • AccountBalanceService.java源码如下,deposit和deduct这两个方法各算各的,丝毫没有考虑当时其他线程对accountBalance的影响
package com.bolingcavalry.service.impl;

import io.quarkus.logging.Log;
import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class AccountBalanceService {

    // 账户余额,假设初始值为100
    int accountBalance = 100;

    /**
     * 查询余额
     * @return
     */
    public int get() {
        // 模拟耗时的操作
        try {
            Thread.sleep(80);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return accountBalance;
    }

    /**
     * 模拟了一次充值操作,
     * 将账号余额读取到本地变量,
     * 经过一秒钟的计算后,将计算结果写入账号余额,
     * 这一秒内,如果账号余额发生了变化,就会被此方法的本地变量覆盖,
     * 因此,多线程的时候,如果其他线程修改了余额,那么这里就会覆盖掉,导致多线程同步问题,
     * AccountBalanceService类使用了Lock注解后,执行此方法时,其他线程执行AccountBalanceService的方法时就会block住,避免了多线程同步问题
     * @param value
     * @throws InterruptedException
     */
    public void deposit(int value) {
        // 先将accountBalance的值存入tempValue变量
        int tempValue  = accountBalance;
        Log.infov("start deposit, balance [{0}], deposit value [{1}]", tempValue, value);

        // 模拟耗时的操作
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        tempValue += value;

        // 用tempValue的值覆盖accountBalance,
        // 这个tempValue的值是基于100毫秒前的accountBalance计算出来的,
        // 如果这100毫秒期间其他线程修改了accountBalance,就会导致accountBalance不准确的问题
        // 例如最初有100块,这里存了10块,所以余额变成了110,
        // 但是这期间如果另一线程取了5块,那余额应该是100-5+10=105,但是这里并没有靠拢100-5,而是很暴力的将110写入到accountBalance
        accountBalance = tempValue;

        Log.infov("end deposit, balance [{0}]", tempValue);
    }

    /**
     * 模拟了一次扣费操作,
     * 将账号余额读取到本地变量,
     * 经过一秒钟的计算后,将计算结果写入账号余额,
     * 这一秒内,如果账号余额发生了变化,就会被此方法的本地变量覆盖,
     * 因此,多线程的时候,如果其他线程修改了余额,那么这里就会覆盖掉,导致多线程同步问题,
     * AccountBalanceService类使用了Lock注解后,执行此方法时,其他线程执行AccountBalanceService的方法时就会block住,避免了多线程同步问题
     * @param value
     * @throws InterruptedException
     */
    public void deduct(int value) {
        // 先将accountBalance的值存入tempValue变量
        int tempValue  = accountBalance;
        Log.infov("start deduct, balance [{0}], deposit value [{1}]", tempValue, value);

        // 模拟耗时的操作
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        tempValue -= value;

        // 用tempValue的值覆盖accountBalance,
        // 这个tempValue的值是基于100毫秒前的accountBalance计算出来的,
        // 如果这100毫秒期间其他线程修改了accountBalance,就会导致accountBalance不准确的问题
        // 例如最初有100块,这里存了10块,所以余额变成了110,
        // 但是这期间如果另一线程取了5块,那余额应该是100-5+10=105,但是这里并没有靠拢100-5,而是很暴力的将110写入到accountBalance
        accountBalance = tempValue;

        Log.infov("end deduct, balance [{0}]", tempValue);
    }
}
  • 接下来是单元
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇从零玩转系列之微信支付实战PC端.. 下一篇quarkus依赖注入之八:装饰器(De..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目