Java实现多线程操作多账户

news2025/2/25 11:37:28

前言

某公司一个面试题:

1.有二十个账户,每个账户初始余额10000元。

2.有十个转账线程,对二十个账户中的两个随机选取账户进行转账,转账额度100以内正整数随机数。

3.每个线程执行100次转账操作。

4.最后请打印出二十个账户的余额。

正好很久没有做这类型题了,拿来练练手,结果碰到了一些问题。

正文

方案一:

首先描述下思路,首先用一个List数组存20个账户,然后对每个账户赋初值10000,在新建10个转账线程,对List数组中20个账户进行并发操作,每个线程分别获取两个账户进行随机额度转账。那么最简单的方式就是对List数组进行加锁就可以了。代码实现如下:

public class test {
    static int num;
    public static void main(String[] args) {
        List<Integer> accountList = new ArrayList<Integer>();
        for (int i = 0;i < 20;i++){
            accountList.add(10000);
        }
        System.out.println(num);

        MulThreadTest mulThreadTest = new MulThreadTest(accountList);
        ThreadGroup threadGroup =new ThreadGroup("ThreadGroup1");
        for (int i = 0;i < 10;i++){
            Thread thread = new Thread(mulThreadTest);
            thread.setName("thread="+i);
            thread.start();
        }
        try{
            Thread.sleep(5000);
        }catch(Exception e){
            e.printStackTrace();
        }
        int sum = 0;
        for (int i = 0;i < 20;i++){
            sum = sum + accountList.get(i);
            System.out.println(accountList.get(i));
        }
        System.out.println(sum);
    }
}
public class MulThreadTest implements Runnable{
    private List<Integer> accountList;
    private  int first;
    private  int second;
    private  int num;

    public MulThreadTest(List accountList){
        this.accountList = accountList;
    }
    @Override
    public void run() {
        synchronized (accountList){
            for(int i = 0;i < 100;i ++) {
                first = new Random().nextInt(20);
                second = new Random().nextInt(20);
                if(first == second){
                    if(second < 19){
                        second += 1;
                    }else {
                        second = 1;
                    }

                }
                num = new Random().nextInt(100);
                if(accountList.get(first) - num < 0){
                    i --;
                    continue;
                }
                System.out.println(Thread.currentThread().getName()+",操作前:"+"账户:"+first+"="+accountList.get(first)+"账户:"+second+"="+accountList.get(second));
                accountList.set(first,accountList.get(first) -  num);
                accountList.set(second,accountList.get(second) +  num);
                System.out.println(Thread.currentThread().getName()+",操作:"+"账户:"+first+"="+accountList.get(first)+"=>"+num+"=>"+"账户:"+second+"="+accountList.get(second));
            }
        }

    }
}

以下是20个账户的金额以及总金额

10114
10046
10268
10676
10250
10344
9899
10120
9629
10145
9211
9761
9903
10187
9374
10508
10046
10005
9529
9985
200000

以上可以解决这个转账并发问题,但是锁的粒度还是有点大了,在一个线程操作List时其它线程是没法操作的,那么如何减小锁的粒度,实现更高的效率呢?

方案二:

在方案一的基础上,把锁的对象修改为List中每个账户,这样当其中一个线程在修改操作账户的时候,其它线程就仅仅无法操作该账户,影响范围会减小很多。

public class test {
    static int num;
    public static void main(String[] args) {
        List<Account> accountList = new ArrayList<Account>();
        for (int i = 0;i < 20;i++){
            accountList.add(new Account(10000));
        }
        System.out.println(num);

        MulThreadTest1 mulThreadTest = new MulThreadTest1(accountList);
        ThreadGroup threadGroup =new ThreadGroup("ThreadGroup1");
        for (int i = 0;i < 10;i++){
            Thread thread = new Thread(mulThreadTest);
            thread.setName("thread="+i);
            thread.start();
        }
        try{
            Thread.sleep(5000);
        }catch(Exception e){
            e.printStackTrace();
        }
        int sum = 0;
        for (int i = 0;i < 20;i++){
            sum = sum + accountList.get(i).getNum();
            System.out.println(accountList.get(i).getNum());
        }
        System.out.println(sum);
    }
}
public class MulThreadTest1 implements Runnable{
    private List<Account> accountList;
    Account firstInteger;
    Account secondInteger;


    public MulThreadTest1(List accountList){
        this.accountList = accountList;
    }
    @Override
    public void run() {
         int first;
         int second;
         int num;
         
            for(int i = 0;i < 100;i ++) {
                first = new Random().nextInt(20);
                second = new Random().nextInt(20);
                if(first == second){
                    if(second < 19){
                        second += 1;
                    }else {
                        second = 1;
                    }

                }
                firstInteger = accountList.get(first);

                    synchronized (firstInteger) {

                        num = new Random().nextInt(100);
                        if (firstInteger.getNum() - num < 0) {
                            i--;
                            continue;
                        }
                        System.out.println(Thread.currentThread().getName() + ",操作前:" + "账户:" + first + "=" + accountList.get(first).getNum() + "账户:" + second + "=" + accountList.get(second).getNum());

                        firstInteger.setNum(firstInteger.getNum() - num);

                        accountList.set(first, firstInteger);

                    secondInteger = accountList.get(second);

                        synchronized (secondInteger) {
                            secondInteger.setNum(secondInteger.getNum() + num);
                            accountList.set(second, secondInteger);
                            //accountList.set(first,accountList.get(first) -  num);
                            //accountList.set(second,accountList.get(second) +  num);
                            System.out.println(Thread.currentThread().getName() + ",操作:" + "账户:" + first + "=" + accountList.get(first).getNum() + "=>" + num + "=>" + "账户:" + second + "=" + accountList.get(second).getNum());
                        }
                
                }

                   

            }

    }
}

然后运行后发现死锁了,再进行修改

public class MulThreadTest1 implements Runnable{
    private List<Account> accountList;
    Account firstInteger;
    Account secondInteger;
    volatile int flag1 = 0;
    volatile int flag2 = 0;


    public MulThreadTest1(List accountList){
        this.accountList = accountList;
    }
    @Override
    public void run() {
         int first;
         int second;
         int num;

            for(int i = 0;i < 100;i ++) {
                first = new Random().nextInt(20);
                second = new Random().nextInt(20);
                if(first == second){
                    if(second < 19){
                        second += 1;
                    }else {
                        second = 1;
                    }

                }
                firstInteger = accountList.get(first);
                if(flag1 == 0) {
                    flag1 = 1;
                    synchronized (firstInteger) {

                        num = new Random().nextInt(100);
                        if (firstInteger.getNum() - num < 0) {
                            i--;
                            continue;
                        }
                        System.out.println(Thread.currentThread().getName() + ",操作前:" + "账户:" + first + "=" + accountList.get(first).getNum() + "账户:" + second + "=" + accountList.get(second).getNum());

                        firstInteger.setNum(firstInteger.getNum() - num);

                        accountList.set(first, firstInteger);

                    }
                    flag1 = 0;
                    secondInteger = accountList.get(second);
                    if(flag2 == 0) {
                        flag2 = 1;
                        synchronized (secondInteger) {
                            secondInteger.setNum(secondInteger.getNum() + num);
                            accountList.set(second, secondInteger);
                            //accountList.set(first,accountList.get(first) -  num);
                            //accountList.set(second,accountList.get(second) +  num);
                            System.out.println(Thread.currentThread().getName() + ",操作:" + "账户:" + first + "=" + accountList.get(first).getNum() + "=>" + num + "=>" + "账户:" + second + "=" + accountList.get(second).getNum());
                        }
                    }
                }

                    flag2 = 0;

            }

    }
}

输出结果发现最终所有账户金额总额不是200000,分析以上代码发现一个问题,线程1对该对象进行操作时,从List中获取账户对象后,线程2也会从List中获取账户对象,然后当线程1抢到锁之前,把线程1中的账户对象覆盖了,这样操作就有问题了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/522039.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

西门子PLC控制步进电机方法与接线(全)

一、步进驱动系统 步进驱动系统包含步进电动机和步进驱动器&#xff0c;前端由PLC发脉冲。 步进电机是将电脉冲信号转变为角位移或线位移以控制转子转动的开环控制电机&#xff08;可以通过安装编码器形成闭环系统&#xff09;。 它旋转是以固定的角度&#xff08;步距角&…

ThinkPHP6的控制器定义及控制器初使用

ThinkPHP6的控制器定义及控制器初使用 控制器定义 控制器文件通常放在controller下面&#xff0c;类名和文件名保持大小写一致&#xff0c;并采用驼峰命名&#xff08;首字母大写&#xff09;。 如果要改变controller目录名&#xff0c;需要在route.php(config/route.php)配…

redis从零开始(1)----五种基本类型:string/hash

认识redis NoSQL Nosql not only sql&#xff0c;泛指非关系型数据库&#xff0c;与之相对的是RDBMS(Relational Database Management System)&#xff0c;即关系型数据库 关系型数据库&#xff1a;列行&#xff0c;同一个表下数据的结构是一样的。 非关系型数据库&#xff…

原生js手动实现一个多级菜单效果(高度可过渡变化)

文章目录 学习链接效果图代码要点 学习链接 vue实现折叠展开收缩动画 - 自己的链接 elment-ui/plus不定高度容器收缩折叠动画组件 - 自己的链接 Vue transition 折叠类动画自动获取隐藏层高度以及手风琴效果实现 vue transition动画钩子- vue官网 vue transition 过渡动画…

vue基础入门

1. vue简介 1.1 什么是vue 官方概念&#xff1a;Vue&#xff08;读音/vju:/&#xff0c;类似于view&#xff09;是一套用于构建用户界面的前端框架 1.2 vue 的特性 vue 框架的特性&#xff0c;主要体现在如下两方面&#xff1a; ① 数据驱动视图 ② 双向数据绑定 数据驱动…

IMS补充业务场景介绍

呼叫保持流程 通话主动Hold的一方,发INVITE消息,媒体流从sendrecv变为sendonly,对方返回200 ok,媒体流从sendrecv变为recvonly,双方ACK后,进入呼叫保持状态,没有通话的RTP包。 大致流程如下 UE A发送INVITE(Sendonly)到网络 网络发送INVITE(Sendonly)到UE B UE发…

Linux文件属性修改

关于我们的文件属性如何修改呢&#xff1f; 我们今天来看一下 chmod chmod u(拥有者)/g(所属组)/o(其他人)(-)r/w/x(t) 文件名 就是这样&#xff0c;我们演示几个 我们想给拥有者去掉file1的读权限 我们file1的拥有者已经没有读权限了&#xff0c;那么我们还想加回来呢…

asp.net+C#基于web的旅游网站自驾游网站

&#xff08;1&#xff09;登录注册模块&#xff1a;输入账号密码&#xff0c;数据库进行验证&#xff0c;正确通过后&#xff0c;根据不同的账户信息&#xff0c;不同角色&#xff0c;获取不同的功能。 &#xff08;2&#xff09;自驾游模块&#xff1a;此模块可以分享自己自…

《计算机网络—自顶向下方法》 第五章Wireshark实验:UDP 协议分析

用户数据报(UDP)协议是运输层提供的一种最低限度的复用/分解服务&#xff0c;可以在网络层和正确的用户即进程间传输数据。UDP 是一种不提供不必要服务的轻量级运输协议&#xff0c;除了复用/分用功能和简单的差错检测之外&#xff0c;几乎就是 IP 协议了&#xff0c;也可以说它…

Python操作Redis常见类型详解

1、windows 上安装 Redis 便于测试&#xff0c;笔者在 windows 上安装 Redis Redis 官方不建议在 windows 下使用 Redis&#xff0c;所以官网没有 windows 版本可以下载。微软团队维护了开源的 windows 版本&#xff0c;对于普通测试使用足够了。 1.1、安装包方式安装 Redis…

万字收藏:《2023网络工程师年度必看书单》

晚上好&#xff0c;我是老杨。 这周是总结周&#xff0c;更新的第三篇内容&#xff0c;还是关于总结的。很多人让我推荐网工适合看的书&#xff0c;其实我推荐过好多次了。 趁着年底&#xff0c;一起把我认为网工适合看的、推荐你看的、值得看的书整理一下&#xff0c;供新老…

视觉SLAM ch13 设计SLAM系统

目录 一、SLAM系统 二、工程框架 三、框架流程 四、具体实现 五、VO整体流程 六、显示整体建图效果 一、SLAM系统 实现一个精简版的双目视觉里程计&#xff0c;前端使用光流法&#xff0c;局部使用局部BA优化。 二、工程框架 app中 run_kitti_stereo.cpp是代码的运行入口…

国内免费可用 ChatGPT 网页版

ChatGPT是一个神奇的机器人&#xff0c;它可以回答任何问题&#xff0c;解决任何问题。它的名字来源于“Chat”和“GPT”&#xff0c;前者代表聊天&#xff0c;后者代表生成预测文本。它被设计成一个智能助手&#xff0c;可以帮助人们解决各种问题。 有一天&#xff0c;一个名…

【Python 爬虫之requests库】零基础也能轻松掌握的学习路线与参考资料

文章目录 一、概述二、Requests 库基本用法三、爬虫中的优秀实践四、参考资料 一、概述 Python 爬虫中&#xff0c;常用来请求网页的库有 urllib、urllib2、httplib等&#xff0c;但是这些库用起来比较麻烦&#xff0c;需要写很多代码。Requests 库正是为了解决这个问题而生的…

Flask轻松构建钉钉接口模版,实现自动化流程优化

项目背景 随着钉钉应用的不断普及和企业数字化程度的提高&#xff0c;越来越多的企业需要开发钉钉接口来完成内部业务流程的自动化和优化。而Flask框架&#xff0c;则是一个轻量级的Python web框架&#xff0c;具有快速开发和灵活性的优势&#xff0c;是钉钉接口开发的理想选择…

python去重列表中相同的字典元素

python去重列表中相同的字典元素 文章目录 python去重列表中相同的字典元素一.知识点二.代码|代码1|问题 |代码2 三.分析总结1、分析2、总结 四.后续代码知识点代码流程问题总结总结 一.知识点 ​ data_list [{“a”: 1, “b”: 2}, {“a”: 2, “b”: 3}, {“a”: 1, “b”:…

华为OD机试真题 Java 实现【相同数字的积木游戏1】【2023Q2 100分】

一、题目描述 小华和小薇一起通过玩积木游戏学习数学。 他们有很多积木&#xff0c;每个积木块上都有一个数字&#xff0c;积木块上的数字可能相同。 小华随机拿一些积木挨着排成一排&#xff0c;请小薇找到这排积木中数字相同且所处位置最远的2块积木块&#xff0c;计算他们…

【C++】——string的模拟实现

前言&#xff1a; 在之前的学习中&#xff0c;我们已经对string类进行了简单的介绍&#xff0c;大家只要能够正常使用即可。但是在面试中&#xff0c;面试官总喜欢让学生自己 来模拟实现string类&#xff0c;最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数…

lightroom磨皮滤镜中文插件Portraiture4最新版本

哈喽&#xff01;小伙伴们&#xff01;整个摄影后期行业都在用Portraiture&#xff0c;这是一个被奉为高级磨皮面板&#xff0c;修图神器、修图的的扩展面板&#xff01;Portraiture这款磨皮插件终于更新啦&#xff01;最近推出了Portraiture4.03版本,新版本光影处理更强大&…

《编程思维与实践》1066.最小不重复数

《编程思维与实践》1066.最小不重复数 题目 思路 一般在oj上循环 2 ⋅ 1 0 9 2\cdot 10^9 2⋅109次以上就会超时,所以由于这题的数据A可以很大,直接循环加一再判断会超时. 优化:首先可以明确要想使不重复数尽可能小,则高位数字应该尽可能小, 即先找到最靠前的两个重复数字,然后…