Java多线程转账
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多线程转账的更多相关文章
- JAVA多线程之中断机制(stop()、interrupted()、isInterrupted())
一,介绍 本文记录JAVA多线程中的中断机制的一些知识点.主要是stop方法.interrupted()与isInterrupted()方法的区别,并从源代码的实现上进行简单分析. JAVA中有3种方 ...
- java多线程(五)之总结(转)
引 如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个 ...
- java 多线程5: java 终止线程及中断机制 (stop()、interrupt() 、interrupted()、isInterrupted())
JAVA中有3种方式可以终止正在运行的线程 ①线程正常退出,即run()方法执行完毕了 ②使用Thread类中的stop()方法强行终止线程.但stop()方法已经过期了,不推荐使用 ③使用中断机制i ...
- java 多线程超详细总结——阿里大牛熬夜整理
引 如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个 ...
- 面试题: java多线程 背1
如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个话其 ...
- 学习笔记-java 多线程
背景说明: 多线程并发与管理,是java基础知识里的重点,本文根据<java核心技术第八版>中的多线程技术的学习,对知识点进行整理:这里只对基础知识点进行简单罗列,以达到对知识点有网状关联 ...
- 想要金九银十面试通关,不懂 Java多线程肯定是不行的!
作者 | 纳达丶无忌 如果对什么是线程.什么是进程仍存有疑惑,请先 Google 之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用 CPU 的资源,因为所有的多线程代码都 ...
- 阅读之Java多线程
Java多线程 用多线程只有一个目的,就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现. 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程 并行与并发: 并行:多个cpu ...
- 【转载】Java多线程
转自:http://www.jianshu.com/p/40d4c7aebd66 引 如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目 ...
- Java多线程| 01 | 线程概述
Java多线程| 01 | 线程概述 线程相关概念 进程与线程 进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是操作系统进行资源分配与调度的基本单位.可以把进程简单的理解 ...
随机推荐
- 西门子PLC高校作业以及创新项目
抢答器 在主持人按下启动按钮,3秒内
- python中可变参数与装饰器的例子
python的可变参数 方法定义 #*args是可以传list类型的可变参数,**kwargs是可以传dict的可变参数 def wrapper(*args, **kwargs): 使用示例 def ...
- 基于.NET+FreeSql实现的仿掘金专栏前后端分离的CMS
前言 今天分享一款基于.NET+FreeSql实现的仿掘金专栏前后端分离.支持Docker部署.集成了OAtuh2授权登录.QQ.Github.Gitee快速登录.简单实用的CMS:lin-cms-d ...
- 深入浅出Java多线程(三):线程与线程组
「引言」 大家好,我是你们的老伙计秀才!今天带来的是[深入浅出Java多线程]系列的第三篇内容:线程与线程组.大家觉得有用请点赞,喜欢请关注!秀才在此谢过大家了!!! 在现代软件开发中,多线程编程已成 ...
- 佳翼M2X16 PCIE4.0X16转4盘M.2NVME固态硬盘 - 我的硬件配置
- CF1916E Happy Life in University 题解
题目: CF1916E Happy Life in University 链接: 洛谷 或者 CF 前置知识点: 线段树与HH的项链 先简单回顾下HH的项链这题怎么做的吧.先去掉莫队算法,因为这个不是 ...
- macOS 上 常用的操作
首先 mac上 若使用的是windows的键盘,那么需要把ctrl 键,设置成 cmd键,因为mac上大多数操作都是 基于cmd键. 1.将ctrl键,修改为cmd键,这样后 复制.粘贴.剪切.全选等 ...
- Delphi Vista,Win7,Win8 的 Uac,管理员身份运行
要用就用下面我自己总结的官方的做法: 1.首先搜到delphi 自带的manifest,然后在其基础上改一个单词 2.将里面的asInvoker改为requireAdministrator 3.修改为 ...
- Spring Boot + MyBatis-Plus 实现 MySQL 主从复制动态数据源切换
MySQL 主从复制是一种常见的数据库架构,它可以提高数据库的性能和可用性.动态数据源切换则可以根据业务需求,在不同场景下使用不同的数据源,比如在读多写少的场景下,可以通过切换到从库来分担主库的压力. ...
- 一键跳转组件所在的文件与具体行数,提升排查效率,分享几个 React Developer Tools 使用小技巧
壹 ❀ 引 React Developer Tools对于很多开发同学可能就是一个检查组件props传递对不对的工具,但事实上它的功能比我们想象的强大.比如我们日常排查问题,常常会遇到想知道某个页面某 ...