[Java手撕]线程安全的转账

news/2024/11/17 15:56:38/文章来源:https://www.cnblogs.com/DCFV/p/18374525

首先来看线程不安全的转账

public class Bank {private int[] accounts;public Bank() {this.accounts = new int[10];for (int i = 0; i < 10; i++) {accounts[i] = 1000;}}public void transfer(int from, int to, int amount) {if (accounts[from] >= amount) {accounts[from] -= amount;accounts[to] += amount;}}public int total() {int count = 0;for (int i = 0; i < 10; i++) {count += accounts[i];}return count;}
}
import java.util.Random;
import java.util.concurrent.CountDownLatch;public class Main {public static void main(String[] args) {Bank bank = new Bank();Random random = new Random();CountDownLatch countDownLatch = new CountDownLatch(10000);for (int i = 0; i < 10000; i++) {new Thread(new Runnable() {@Overridepublic void run() {int from = random.nextInt(10);int to = random.nextInt(10);int amount = random.nextInt(1000);bank.transfer(from, to, amount);System.out.println(Thread.currentThread().getName() + " 从 " + from + " 转账到 " + to + " " + amount + " 元");countDownLatch.countDown();}}).start();}try {countDownLatch.await();System.out.println("银行最终总余额 = " + bank.total());} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

image

线程安全的转账

竞争太激烈,所有线程都在抢同一把银行锁

public class Bank {private int[] accounts;public Bank() {this.accounts = new int[10];for (int i = 0; i < 10; i++) {accounts[i] = 1000;}}public synchronized void transfer(int from, int to, int amount) {if (accounts[from] >= amount) {accounts[from] -= amount;accounts[to] += amount;}}public int total() {int count = 0;for (int i = 0; i < 10; i++) {count += accounts[i];}return count;}
}
import java.util.Random;
import java.util.concurrent.CountDownLatch;public class Main {public static void main(String[] args) {Bank bank = new Bank();Random random = new Random();CountDownLatch countDownLatch = new CountDownLatch(10000);for (int i = 0; i < 10000; i++) {new Thread(new Runnable() {@Overridepublic void run() {int from = random.nextInt(10);int to = random.nextInt(10);int amount = random.nextInt(1000);bank.transfer(from, to, amount);System.out.println(Thread.currentThread().getName() + " 从 " + from + " 转账到 " + to + " " + amount + " 元");countDownLatch.countDown();}}).start();}try {countDownLatch.await();System.out.println("银行最终总余额 = " + bank.total());} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

image

线程安全的转账升级版

分散锁到各个账户上去,每个线程只要取得两个账户上的锁即可,
要注意按顺序获取锁,否则会死锁

import java.util.concurrent.locks.ReentrantLock;public class Account {private int money;private ReentrantLock lock;public Account(int money){this.money = money;lock = new ReentrantLock();}public ReentrantLock getLock() {return lock;}public int getMoney() {return money;}public void sub(int money){this.money -= money;}public void add(int money){this.money += money;}
}
import java.util.ArrayList;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;public class Bank {private Account[] accounts;public Bank(int num){this.accounts = new Account[1000];for(int i = 0;i<num;i++){this.accounts[i] = new Account(1000);}}public void transfer(int from ,int to ,int amount){ReentrantLock lock1,lock2;if (from < to){lock1 = accounts[from].getLock();lock2 = accounts[to].getLock();}else if(from > to){lock2 = accounts[from].getLock();lock1 = accounts[to].getLock();}else{return;}synchronized (lock1){synchronized (lock2){if(accounts[from].getMoney()>=amount){accounts[from].sub(amount);accounts[to].add(amount);}}}}public int total(){int count = 0;for (int i = 0;i<this.accounts.length ;i++){count += accounts[i].getMoney();}return count;}}
import java.sql.Time;
import java.util.Random;
import java.util.concurrent.CountDownLatch;public class Main {public static void main(String[] args) {Bank bank = new Bank(1000);Random random = new Random();CountDownLatch countDownLatch = new CountDownLatch(1000);long start = System.nanoTime();for (int i = 0; i < 1000; i++) {new Thread(new Runnable() {@Overridepublic void run() {int from = random.nextInt(10);int to = random.nextInt(10);int amount = random.nextInt(1000);bank.transfer(from, to, amount);System.out.println(Thread.currentThread().getName() + " 从 " + from + " 转账到 " + to + " " + amount + " 元");countDownLatch.countDown();}}).start();}try {countDownLatch.await();long end = System.nanoTime();System.out.println("程序运行总时间 "+ (end - start)/1000000 + "毫秒");System.out.println("银行最终总余额 = " + bank.total());} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

image

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入解析财务报表:如何抓住关键指标作出明智决策

一、概述 财务报表中包含了丰富的信息,但如果在分析时缺乏明确的思路或忽略重点,很容易被复杂的数据搞得无所适从。本文将介绍财务报表中的关键指标,包括资产负债率的分析、净资产收益率的解读,以及销售复合增长率的计算,帮助大家有针对性地理解和学习这些内容。二、关键指…

第三章 redis数据类型

redis数据类型redis可以理解成一个全局的大字典,key就是数据的唯一标识符。根据key对应的值不同,可以划分成5个基本数据类型。redis = {"name":"yuan","scors":["100","89","78"],"info":{"name…

5. 概述(General description)

5. 概述(General description) 5.1. Introduction 5.1. A low-rate wireless personal area network (LR-WPAN) is a simple, low-cost communication network that allows wireless connectivity in applications with limited power and relaxed throughput requirements. …

jmeter中提取token值(正则表达式,)

jmeter中提取token值(正则表达式) 一、接口前准备 案例链接:http://shop.duoceshi.com/ uiid接口: http://manage.duoceshi.com/auth/code get请求 登录接口: http://manage.duoceshi.com/auth/login POST请求 请求参数: {"username":"admin","…

操作系统-线程

一、线程介绍线程是操作系统能内够进行运算、执行的最小单位,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。​ 总结:线程是进程的一部分,是进程内负责执行的单位,进程…

[学习笔记]在不同项目中切换Node.js版本

@目录使用 Node Version Manager (NVM)安装 NVM使用 NVM 安装和切换 Node.js 版本为项目指定 Node.js 版本使用环境变量指定 Node.js安装多个版本的 Node.js设置环境变量验证配置使用 npm 脚本切换 在开发中,可能会遇到不同的Vue项目需要不同的Node.js,在开发机上如何快速切换…

【HW系列+技战法】内存马的Webshell联合对抗技战法

原创 BeatRex的成长记录一、技战法概述二、Webshell对抗手段2.1 落地文件型Webshell检测与对抗2.1 无文件型内存马检测与对抗2.3 Webshell免杀对抗一、技战法概述 Webshell是黑客经常使用的一种后门,其目的是获得服务器的执行操作权限,常见的Webshell编写语言为ASP、JSP、PHP…

【待做】【ByPass系列】php的webshell绕过方法总结

原创 掌控安全EDU一、php的异或运算 $a="~+d()"^"!{+{}"这个表示了$a=这两个字符串之间进行一个异或运算 运算异或运算符,按二进制位进行异或运算 这里的运算会把符号转化为ascii码,再转化为二进制,再转化为十进制进行运算,再把结果转化为ascii码 通过…

PCA原理与水果成熟状态数据分析实例:Python中PCA-LDA 与卷积神经网络CNN

全文链接:https://tecdat.cn/?p=37450 主成分分析(PCA)作为数据科学中用于可视化和降维的重要工具,在处理具有大量特征的数据集时非常有用。就像我们难以找到时间阅读一本 1000 页的书,而更倾向于 2 到 3 页的总结以抓住整体概貌一样,当数据集中特征过多时,PCA 可以帮…

[题解] permutation

[题解] Permutation解析一眼 DP 或者 组合。70pts 场上推的DP 对于 \((4,2,2)\),先把所有序列枚举出来: \[\begin{split} 1\ \ \ 2\\ 1\ \ \ 3\\ 1\ \ \ 4\\ --\\ 2\ \ \ 3\\ 2\ \ \ 4\\ 3\ \ \ 4 \end{split} \]可以发现,对于分割线上的部分,可以看作 \((3,1,1)\) 的所有序…