首页 > 编程语言 >[Java手撕]线程安全的转账

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

时间:2024-08-22 20:37:12浏览次数:14  
标签:转账 Java int random amount 线程 accounts new public

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

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() {
                @Override
                public 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() {
                @Override
                public 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() {
                @Override
                public 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

标签:转账,Java,int,random,amount,线程,accounts,new,public
From: https://www.cnblogs.com/DCFV/p/18374525

相关文章

  • 一起学Java(3)-Java项目构建工具Gradle和Maven场景定位和优缺点对比
    在第一步创建的项目(java-all-in-one)项目里,我们提到了使用Gradle作为项目构建工具。看到这里,不知道你是否有疑惑,什么是项目构建工具。Java项目常用构建工具有哪些?都有什么特点?带着疑惑,本文对Java的构建工具进行一些梳理,以同步理解。从而使大家对我们项目中使用到的技术栈和技术......
  • Java多态
    1.多态1.1多态的概述(记忆)什么是多态不同类的对象在调用同一个方法时表现出来的多种不同行为多态的前提要有继承或实现关系要有方法的重写要有父类引用指向子类对象代码演示classAnimal{  publicvoideat(){    System.out.println("动物吃肉")......
  • 操作系统-线程
    一、线程介绍线程是操作系统能内够进行运算、执行的最小单位,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。​ 总结:线程是进程的一部分,是进程内负责执行的单位,进程是由资源单位(......
  • Java设计模式之代理模式:静态代理VS动态代理,与其他模式的对比分析和案例解析
    一、代理模式简介代理模式(ProxyPattern)是一种结构型设计模式,它提供了一个代理对象,用来控制对另一个对象的访问。这种模式通常用于在访问对象时引入额外的功能,而不改变对象的接口。代理模式的核心思想是为其他对象提供一种代理,以控制对这个对象的访问。在现实生活中,代理模......
  • java 面向对象4
    1.java存在抽象类,不写函数的具体实现,需要再函数前边加上abstract修饰.这个和c++语法存在差别,但是同样抽象类不可以创建对象,可以用抽象类的引用指向子类创建的对象,这个用法在一定程度上使得接口一致.利于在开发中保持接口一致,避免接口混乱的问题.同时利用抽象类可以使得一些......
  • JavaSE基础知识分享(十三)
    写在前面今天继续讲Java中的网络编程的知识!网络编程概述计算机网络计算机网络是将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统、网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。例如:极域。网络......
  • Java中的static关键字
    static可以用来修饰成员变量,也能修饰成员方法。1.1static修饰成员变量Java中的成员变量按照有无static修饰分为两种:类变量、实例变量。它们的区别如下图所示: 由于静态变量是属于类的,只需要通过类名就可以调用:类名.静态变量实例变量是属于对象的,需要通过对象才能调用:对......
  • Java基础——自学习使用(static关键字)
    一、static关键字是什么?static修饰的代码属于类,定义的变量存储在方法区的静态常量池当中二、static可以修饰什么1.static修饰变量static修饰的变量叫做类变量,被所有该类产生的对象所共享,存储在方法区的静态常量池中2.static修饰方法static修饰是的方法属于静态方法,stat......
  • Java中对继承的学习
    继承目录继承继承的概念为什么需要继承继承的特性继承的优点和缺点继承的概念继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为......
  • Java并发编程(八)
    1、volatile有什么用?能否用一句话说明下volatile的应用场景volatile保证内存可见性和禁止指令重排。volatile用于多线程环境下的单次操作(单次读或者单次写)。2、为什么代码会重排序在执行程序时,为了提供性能,处理器和编译器常常会对指令进行重排序,但是不能随意重排......