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)是计算机中的程序关于某数据集合上的一次运行活动,是操作系统进行资源分配与调度的基本单位.可以把进程简单的理解 ...
随机推荐
- OCR文字检测与识别系统:融合文字检测、文字识别和方向分类器的综合解决方案
1. OCR文字检测与识别系统:融合文字检测.文字识别和方向分类器的综合解决方案 前两章主要介绍了DBNet文字检测算法以及CRNN文字识别算法.然而对于我们实际场景中的一张图像,想要单独基于文字检测 ...
- Linux-CentOS7 SSH免登陆
配置三台服务器,如下: 生成密钥 在三台机器(node1\node2\node3)执行以下命令,生成公钥与私钥. ssh-keygen -t rsa 执行该命令之后,按下三个回车即可,然后敲(三个回车 ...
- 华硕ROG Zenith Extreme Alpha主板评测:主板界的航空母舰
在AMD推第一代锐龙线程撕裂者的时期,华硕ROG Zenith Extreme主板凭借给力的用料,各种便利的超频设计,出色的SupermeFX音效还有万兆网卡赢得了不少用户的好评.现在AMD推出了第二 ...
- 绝对凶器!NVIDIA Titan Z细节全曝光
AMD Radeon R9 295X2已经拉开了新一代双芯卡皇大战的序幕,接下来就等NVIDIA GeForce GTX Titan Z的登场了.最新消息显示,它比原计划推迟了一个星期,将在5月8日正 ...
- 单片机 IAP 技术方案设计
1.前言 关于 IAP 技术,做过 bootloader 的想必很熟悉 (IAP全称 In Application Programming,即应用编程),和 ISP (全称 In System Pro ...
- 好书推荐之《码出高效》、《阿里巴巴JAVA开发手册》
好评如潮 <阿里巴巴Java开发手册> 简介 <阿里巴巴Java开发手册>的愿景是码出高效,码出质量.它结合作者的开发经验和架构历程,提炼阿里巴巴集团技术团队的集体编程经验和软 ...
- 如何做好一个基础的搜索功能?记一个因客户大数据量而导致的后发先至Bug
壹 ❀ 引 上篇文章算是开了一个新系列,因为工作缘故,我基本每天都在跟各式各样的bug打交道.其实站在一个开发的角度,我想每个人应该都更喜欢创造新代码,创造新bug,而不是每天都泡在茫茫代码海洋中定位 ...
- 西门子SIMATIC LPMLV30 库的模式和状态管理器
从基于S7-1200 / S7-1500的OMAC PackML V3.0获取到的文章内容,用于记录查看 基本信息 根据PackML_V3.0,该库包含了用于机械模式和状态管理器的功能模块. • 机械 ...
- Codeforces Round #825 (Div. 2) A-D
比赛链接 A 题解 知识点:贪心. 考虑两种方法: 所有不同的位置使用操作1变成相同 使用操作1将两串01数量相同,然后使用1次操作2 取其中最小的即可. 时间复杂度 \(O(n)\) 空间复杂度 \ ...
- 【Unity3D】伽马校正
1 伽马相关概念 1.1 人眼对亮度变化的感知 人眼对亮度变化的感知不是线性的,如下图,人眼对亮区的亮度变化不太敏感,对暗区的亮度变化较敏感.另外,我们可以想象一下,在一个黑暗的房间里,由 1 根 ...