Java多线程转账

关键词:多线程,Java

以前的一道面试题,要求是使用Java多线程,实现一个转账业务。不考虑数据库,不考虑其他第三方系统。只考虑当前Java程序内各个账户进行转账,保证转账金额正确性和转账功能效率。

想起那大约还是两年前,是线上面试,面试官给完题目就关闭视频通话,让我自己去写代码,并且告知可以看浏览器。

要是放到现在可不行了哈!直接ChatGPT,分分钟就写好了,而且各种说辞都能准备好!


实现代码

代码地址

这里使用充血模型:Account

package transfer;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock; /**
* @author keboom
* @date 2021/8/24
*/
public class Account { private Long id;
private double balance;
private ReentrantLock lock = new ReentrantLock();
// 记录转账次数,没什么用,只是为了验证transfer执行的次数
AtomicInteger count = new AtomicInteger(0); public Account(Long id, double balance) {
this.id = id;
this.balance = balance;
} public void deposit(double amount) {
lock.lock();
balance += amount;
lock.unlock();
} public void withdraw(double amount) {
lock.lock();
balance -= amount;
lock.unlock();
} public double getBalance() {
return balance;
} public boolean transfer(Account target, double amount) {
if (this == target) {
System.out.println("不能自己转给自己");
return false; // 防止自己向自己转账
} if (this.getBalance() < amount) {
System.out.println("余额不足,转账失败");
return false; // 余额不足,转账失败
}
// avoid deadlock
// 使用两把锁,按照账户的 hashcode 顺序来避免死锁
Account firstLock = this.hashCode() > target.hashCode() ? this : target;
Account secondLock = this.hashCode() > target.hashCode() ? target : this;
boolean flag = true;
while (flag) {
if (firstLock.lock.tryLock()) {
try {
if (secondLock.lock.tryLock()) {
try {
this.withdraw(amount);
target.deposit(amount);
count.incrementAndGet();
flag = false;
Thread.sleep(1);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
secondLock.lock.unlock();
}
}
} finally {
firstLock.lock.unlock();
}
}
}
return true;
} }

首先是余额的正确性,balance 并不需要使用 volatile 关键字修饰,因为 balance 变量的访问是通过锁来保护的。在使用 ReentrantLock 进行加锁和解锁的过程中,会保证对 balance 变量的读取和写入操作在同一时刻只能被一个线程访问,从而确保了线程间的可见性和原子性。

转账方法,会有多线程进行调用,因此需要锁来控制。为了防止死锁问题,我们通过hash值来得到一个加锁顺序。

接下来使用线程池并发调用转账方法:

package transfer;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; /**
* 这个平台来进行转账操作
*
* @author keboom
* @date 2023/11/28
*/
public class TransferTask { public static void main(String[] args) throws InterruptedException {
Account accountA = new Account(1L, 100000);
Account accountB = new Account(1L, 100000); ExecutorService toA = Executors.newFixedThreadPool(3);
ExecutorService toB = Executors.newFixedThreadPool(3); for (int i = 0; i < 1000; i++) {
toB.execute(() -> {
accountB.transfer(accountA, 10);
});
} for (int i = 0; i < 1000; i++) {
toA.execute(() -> {
accountA.transfer(accountB, 10);
});
} toA.shutdown();
toB.shutdown(); toA.awaitTermination(1, TimeUnit.MINUTES);
toB.awaitTermination(1, TimeUnit.MINUTES);
System.out.println("accountA: " + accountA.getBalance()+ " " + accountA.count);
System.out.println("accountB: " + accountB.getBalance()+ " " + accountB.count); }
}

这里我们总共用6个线程去模拟。因为同一时刻只有一个线程能执行转账操作。如果线程过多,会发生大量的线程争抢,会有一些线程饿死。

比如我实验了使用20个线程并发执行转账。发现一个是执行时间变得很长,而且到最后返回时结果也不正确,发现执行次数少了。猜测应该是一些线程饿死,导致一些任务并没有执行。

Java多线程转账的更多相关文章

  1. JAVA多线程之中断机制(stop()、interrupted()、isInterrupted())

    一,介绍 本文记录JAVA多线程中的中断机制的一些知识点.主要是stop方法.interrupted()与isInterrupted()方法的区别,并从源代码的实现上进行简单分析. JAVA中有3种方 ...

  2. java多线程(五)之总结(转)

    引 如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个 ...

  3. java 多线程5: java 终止线程及中断机制 (stop()、interrupt() 、interrupted()、isInterrupted())

    JAVA中有3种方式可以终止正在运行的线程 ①线程正常退出,即run()方法执行完毕了 ②使用Thread类中的stop()方法强行终止线程.但stop()方法已经过期了,不推荐使用 ③使用中断机制i ...

  4. java 多线程超详细总结——阿里大牛熬夜整理

    引 如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个 ...

  5. 面试题: java多线程 背1

    如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个话其 ...

  6. 学习笔记-java 多线程

    背景说明: 多线程并发与管理,是java基础知识里的重点,本文根据<java核心技术第八版>中的多线程技术的学习,对知识点进行整理:这里只对基础知识点进行简单罗列,以达到对知识点有网状关联 ...

  7. 想要金九银十面试通关,不懂 Java多线程肯定是不行的!

    作者 | 纳达丶无忌 如果对什么是线程.什么是进程仍存有疑惑,请先 Google 之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用 CPU 的资源,因为所有的多线程代码都 ...

  8. 阅读之Java多线程

    Java多线程 用多线程只有一个目的,就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现. 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程 并行与并发: 并行:多个cpu ...

  9. 【转载】Java多线程

    转自:http://www.jianshu.com/p/40d4c7aebd66 引 如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目 ...

  10. Java多线程| 01 | 线程概述

    Java多线程| 01 | 线程概述 线程相关概念 进程与线程 进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是操作系统进行资源分配与调度的基本单位.可以把进程简单的理解 ...

随机推荐

  1. Dto中使用正则校验规则,保证传入数据的正确性

    使用RegularExpression

  2. DevelopTool

    目录 01-PostMan常用玩法详解

  3. 6张图表 + 1个案例 带你入门tcpdump的使用和原理

    一.tcpdump简介 tcpdump是什么? 来看看 tcpdump官网怎么说:This is the home web site of tcpdump, a powerful command-li ...

  4. HT UI 5.0,前端组件图扑是认真的

    为顺应数字时代的不断发展,图扑 HT UI 5.0 在原有功能强大的界面组件库的基础上进行了全面升级,融入了更先进的技术.创新的设计理念以及更加智能的功能.HT UI 5.0 使用户体验更为直观.个性 ...

  5. 遥感图像处理笔记之【U-Net for Semantic Segmentation on Unbalanced Aerial Imagery】

    遥感图像处理学习(5) 前言 遥感系列第5篇.遥感图像处理方向的学习者可以参考或者复刻 本文初编辑于2023年12月15日 2024年1月24日搬运至本人博客园平台 文章标题:U-Net for Se ...

  6. 设计模式(三十二)----综合应用-自定义Spring框架-自定义Spring IOC-自定义Spring IOC总结

    1 自定义Spring IOC总结 1.1 使用到的设计模式 工厂模式.这个使用工厂模式 + 配置文件的方式. 单例模式.Spring IOC管理的bean对象都是单例的,此处的单例不是通过构造器进行 ...

  7. windows 10 制作招聘系统镜像

    我一直以来都有个想法,就是彻底攻破重装系统这块,但是一直没有时间 没有攻破,今天终于攻破.参考了文章:https://www.cnblogs.com/del88/p/12667087.html 需求: ...

  8. windows_exporter 安装

    windows_exporter 安装 背景 如果想使用Prometheus监控Windows主机相关参数,那么就需要在Windows系统的主机上进行安装指标收集器. windows_exporter ...

  9. gif 制作

    gif 制作 博文中使用 gif 有时比纯粹的图片更明了.比如展示"墨刀"中的动画效果: 录制视频 首先利用录制视频,例如使用在线录制工具 vizard. Tip:需要花费2分钟手 ...

  10. es6 快速入门 系列 —— 模块

    其他章节请看: es6 快速入门 系列 模块 es6 以前,每个 javascript 都共享这一个全局作用域,随着代码量的增加,容易引发一些问题,比如命名冲突. 其他语言有包这样的概念来定义作用域, ...