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)是计算机中的程序关于某数据集合上的一次运行活动,是操作系统进行资源分配与调度的基本单位.可以把进程简单的理解 ...
随机推荐
- NLP文本匹配任务Text Matching [无监督训练]:SimCSE、ESimCSE、DiffCSE 项目实践
NLP文本匹配任务Text Matching [无监督训练]:SimCSE.ESimCSE.DiffCSE 项目实践 文本匹配多用于计算两个文本之间的相似度,该示例会基于 ESimCSE 实现一个无监 ...
- 1.10 内存ShellCode注入与格式化
ShellCode 的格式化与注入功能在实战应用中也尤为重要,格式化Shellcode是指将其转换为可执行的二进制格式,使其能够在内存中运行.注入Shellcode是指将格式化的Shellcode注入 ...
- 【奶奶看了都会】云服务器ChatGLM模型fine-tuning微调,让你拥有自己的知识库
1.背景 大家好啊,上次给大家写了ChatGLM-6B的部署使用教程,[奶奶看了都会]云服务器部署开源ChatGLM-6B,让你拥有自己的ChatGPT 但是因为模型比较小的问题,所以日常工作中可能用 ...
- public private protected 的辨析
一. public 1.作为类内成员的访问修饰符时,由public修饰的成员数据或者成员函数可以在类外(即派生类内以及实例化的对象后)以及类内进行随意访问 可以看到public成员Data在类外是可访 ...
- 小知识:MAC上添加小米喷墨打印机
最近新购一个小米喷墨打印机,价格不贵,可彩打资料,也能打印照片,非常提升家庭幸福感的一件物品: 如果使用手机打印,下载米家打印就非常方便了. 但是有时候需要电脑打印,使用自己电脑添加打印机时遇到一些小 ...
- IIS创建和管理虚拟网站
实验介绍: 本文会详细介绍创建虚拟站点的三种方法 一:IP地址建立站点 1.打开安装了IIS的windows,进入ip配置页面. 添加几个ip,我这里添加的是192.168.1.209,192.168 ...
- FreeSWITCH添加g729编码及pcap音频提取
操作系统 : debian 11 (bullseye,docker).Windows10_x64 FreeSWITCH版本 :1.10.9 Docker版本:23.0.6 Python 版本 : ...
- CentOS7环境源码安装freeswitch1.10.7
操作系统 :CentOS 7.6_x64 freeswitch版本 :1.10.7 一.安装步骤 1.下载freeswitch源代码 wget http://files.freeswitch.org/ ...
- jvm的简介
什么是jvm? java虚拟机就是二进制字节码的运行环境.我们可以把jvm看做是运行在不同系统上的一个软件应用的计算机,就比如说我们要打开图片,就得用看图软件,或者我们要对文件进行解压,是不是得用解压 ...
- NC19246 数据结构
题目链接 题目 题目描述 qn姐姐最好了~ qn姐姐给你了一个长度为n的序列还有m次操作让你玩, 1 l r 询问区间[l,r]内的元素和 2 l r 询问区间[l,r]内的元素的平方和 3 l r ...