java基础入门-多线程同步浅析-以银行转账为例子

在说之前先普及一下线程是什么?

线程:说白了就是一个任务片段

进程:是一个具有独立功能的程序关于某个数据集合的一次运行活动,一个进程有一个或者多个线程

线程与进程的本质区别就是有么有数据共享空间,线程之间可以共享数据,进程不可以

下面进入主题:线程间的同步

由于现在业务流程增加,业务节点也增加,使用业务的人员也同时增加,这个时候就不可避免的出现并发问题,多个线程同时访问操作某一个数据单元

我们以银行转账为例说明,下面先上代码:

建立一个银行的类,里面主要包括三个方法,一个是转账,一个是得到现有银行存款总数,一个是得到现在存户数量

public class Bank {

	private final double[] accounts;

	public Bank(int n, double initialBalance) {
		accounts = new double[n];
		for (int i = 0; i < accounts.length; i++) {
			accounts[i] = initialBalance;
		}
	}

	public void transfer(int from, int to, double amount) {
		if (accounts[from] < amount) {
			return;
		}
		System.out.println(Thread.currentThread());
		accounts[from] -= amount;
		System.out.printf("%f from %d to %d ", amount, from, to);
		accounts[to] += amount;
		System.out.println("total:" + getTotalBalance());
	}

	public double getTotalBalance() {
		double sum = 0d;
		for (int i = 0; i < accounts.length; i++) {
			sum += accounts[i];
		}
		return sum;
	}

	public int getAccountSize() {
		return accounts.length;
	}
}

下面是转账类,因为需要并发操作,所以实现Runnable接口

public class TransferRunnable implements Runnable {

	private Bank bank;
	private int fromAccount = 0;
	private double maxAmount = 0;

	public TransferRunnable(Bank b, int fromAccount, double maxAmount) {
		this.bank = b;
		this.fromAccount = fromAccount;
		this.maxAmount = maxAmount;
	}

	@Override
	public void run() {
		double amount = maxAmount * Math.random();
		int toAccount = (int) ((int) bank.getAccountSize() * Math.random());
		bank.transfer(fromAccount, toAccount, amount);
		try {
			Thread.sleep((long) (100L * Math.random()));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

下面是测试类:

public class Test {
	public static void main(String[] args) {
		Bank bank = new Bank(100, 1000);
		for (int i = 0; i < 3; i++) {
			TransferRunnable transferRunnable = new TransferRunnable(bank, i,
					1000);
			Thread thread = new Thread(transferRunnable);
			thread.start();
		}
	}
}

输出结果:

Thread[Thread-0,5,main]

Thread[Thread-2,5,main]

Thread[Thread-1,5,main]

430.796266 from 0 to 75

714.274395 from 1 to 88

849.880218 from 2 to 33

total:98435.8453871716

total:99150.11978192833

total:100000.0

我们看上面的结果,特别是最后三行的total总数,发现,第一第二次转账后,总数不对了,仔细观察打印结果,由于并行执行任务,而且中间由于是由cup分配执行顺序,所以我们看到的结果并没有完全按照我们的方法所实现的那样输出出来

由于出现这样的问题,我们引入“锁”的概念,由于这里面是浅析,就不针对锁详细说明,下面我们在bank类里面的转账方法上面加上最简单最常用的锁synchronized,看看结果是怎样:

public class Bank {

	private final double[] accounts;

	public Bank(int n, double initialBalance) {
		accounts = new double[n];
		for (int i = 0; i < accounts.length; i++) {
			accounts[i] = initialBalance;
		}
	}

	//加了锁
	public synchronized void transfer(int from, int to, double amount) {
		if (accounts[from] < amount) {
			return;
		}
		System.out.println(Thread.currentThread());
		accounts[from] -= amount;
		System.out.printf("%f from %d to %d ", amount, from, to);
		accounts[to] += amount;
		System.out.println("total:" + getTotalBalance());
	}

	public double getTotalBalance() {
		double sum = 0d;
		for (int i = 0; i < accounts.length; i++) {
			sum += accounts[i];
		}
		return sum;
	}

	public int getAccountSize() {
		return accounts.length;
	}
}

输出结果:

Thread[Thread-0,5,main]

187.754955 from 0 to 50 total:100000.0

Thread[Thread-1,5,main]

282.138799 from 1 to 90 total:100000.0

Thread[Thread-2,5,main]

217.089515 from 2 to 86 total:100000.00000000001

上面的输出结果基本一致,最后一个结果出现小数问题,主要是由于我数据精度的问题,后续可以通过其他设置来避免。

程序里面由于出现了锁,所以在性能上面不可避免的出现下降,特别是在一些大型程序里面,所以这里面需要根据实际业务所需,把锁的范围锁到比较小的范围,使得性能不会大幅度的下降。

最后我们把上面的东西总结出一个图

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-27 18:22:20

java基础入门-多线程同步浅析-以银行转账为例子的相关文章

java基础入门-多线程同步浅析-以银行转账为样例

在说之前先普及一下线程是什么? 线程:说白了就是一个任务片段 进程:是一个具有独立功能的程序关于某个数据集合的一次执行活动.一个进程有一个或者多个线程 线程与进程的本质差别就是有么有数据共享空间.线程之间能够共享数据.进程不能够 以下进入主题:线程间的同步 因为如今业务流程添加.业务节点也添加.使用业务的人员也同一时候添加.这个时候就不可避免的出现并发问题,多个线程同一时候訪问操作某一个数据单元 我们以银行转账为例说明,以下先上代码: 建立一个银行的类,里面主要包含三个方法,一个是转账,一个是得

Java基础入门 传智播客pdf

下载地址:网盘下载 内容简介 <Java基础入门>从初学者的角度详细讲解了Java开发中重点用到的多种技术.全书共11章,包括Java开发环境的搭建及其运行机制.基本语法.面向对象的思想,采用典型翔实的例子.通俗易懂的语言阐述面向对象中的抽象概念.在多线程.常用API.集合.IO.GUI.网络编程章节中,通过剖析案例.分析代码结构含义.解决常见问题等方式,帮助初学者培养良好的编程习惯.最后,讲解了Eclipse开发工具,帮助初学者熟悉开发工具的使用.  <Java基础入门>附有配套

Java学习笔记---多线程同步的五种方法

一.引言 前几天面试,被大师虐残了,好多基础知识必须得重新拿起来啊.闲话不多说,进入正题. 二.为什么要线程同步 因为当我们有多个线程要同时访问一个变量或对象时,如果这些线程中既有读又有写操作时,就会导致变量值或对象的状态出现混乱,从而导致程序异常.举个例子,如果一个银行账户同时被两个线程操作,一个取100块,一个存钱100块.假设账户原本有0块,如果取钱线程和存钱线程同时发生,会出现什么结果呢?取钱不成功,账户余额是100.取钱成功了,账户余额是0.那到底是哪个呢?很难说清楚.因此多线程同步就

Java基础入门 目录:

本系列博文内容主要是自己日常工作学习中常见.注意.以及还需要巩固的内容,有些本人熟练掌握的内容不在该博文内,也不能当作查询技术的"海词"使用,主要用于学习并记录成长!!! Java开发入门 Java编程基础 Java API

Java 基础入门

Java基础 JVM.JDK.JRE三者的关系: JDK:(Java Develop Kit)主要是针对开发人员使用的,是整个java的核心.它包括了Java运行环境(JRE),Java工具( javac/java/jdb等 )和Java基础类库. JRE:(Java Runtime Environment)是运行java程序必须的环境的集合.它包括了JVM标准实现和Java核心类库. JVM:(Java Virtual Machine)java虚拟机,java本身不跨平台,java跨平台是依靠

JAVA基础入门通关检测

一.JAVA基础知识点问答(1.2.3章) 1. JDK1.6.0_06软件的安装.(请安装在 C:\Java 目录下) 2. JAVA的开发环境如何配置?(即:JAVA_HOME . Path . CLASSPATH 的配置) 3. 开发一个JAVA应用程序的步骤是?(如下程序) public class FirstSample { public static void main(String[] args ) { System.out.print ("欢迎你们来北京,来到"); S

Java基础之多线程(二)

线程的同步:银行取现操作.. Java中实现线程同步通过synchronized关键字定义,而这关键字可以通过两种方法 同步代码块:(Java中有四种代码块,普通代码块.构造块.静态代码块(static).同步块) 同步方法: 线程的异步:存在线程安全问题.. 示例:生产者与消费者的问题? 原文地址:https://www.cnblogs.com/in-the-game-of-thrones/p/11308233.html

【BigData】Java基础_多线程

所谓的多线程就像小吃街卖铜锅米线的老板,老板就好比一个操作系统,如果5个顾客同时去买米线,那么,此时老板就会同时准备好5个铜锅,然后同时放上水.调料.米线,同时煮5个人的米线,如果逐个煮的话,那么估计煮了2个人的米线,后面3人就来找老板退款了. 官方解释:多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术 在Java中实现多线程四部曲: (1)将需要用多线程方式执行的逻辑,写入一个runnable实现类中(run方法中): (2)创建出这个runnabl

重学JAVA基础(六):多线程的同步

1.synchronized关键字 /** * 同步关键字 * @author tomsnail * @date 2015年4月18日 下午12:12:39 */ public class SyncThreadTest { private static final byte[] lock = new byte[1]; /** * 同步方法 * @author tomsnail * @date 2015年4月18日 下午12:15:30 */ public synchronized void te