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
上面的输出结果基本一致,最后一个结果出现小数问题,主要是因为我数据精度的问题,兴许能够通过其它设置来避免。
程序里面因为出现了锁,所以在性能上面不可避免的出现下降,特别是在一些大型程序里面。所以这里面须要依据实际业务所需,把锁的范围锁到比較小的范围,使得性能不会大幅度的下降。
最后我们把上面的东西总结出一个图
java基础入门-多线程同步浅析-以银行转账为样例的更多相关文章
- Java 基础入门随笔(10) JavaSE版——单例设计模式
设计模式:对问题行之有效的解决方式.其实它是一种思想. 1.单例设计模式. 解决的问题:就是可以保证一个类在内存中的对象唯一性.(单个实例) 使用单例设计模式需求:必须对于多个程序使用同一个配置信息对 ...
- Java基础技术多线程与并发面试【笔记】
Java基础技术多线程与并发 什么是线程死锁? 死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去,我们就可以称 ...
- java 又一次抛出异常 相关处理结果演示样例代码
java 又一次抛出异常 相关处理结果演示样例代码 package org.rui.ExceptionTest; /** * 又一次抛出异常 * 在某些情况下,我们想又一次掷出刚才产生过的违例,特别是 ...
- Java基础之多线程篇(线程创建与终止、互斥、通信、本地变量)
线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ...
- 关于java基础、多线程、JavaWeb基础、数据库、SSM、Springboot技术汇总
作者 : Stanley 罗昊 本人自行总结,纯手打,有疑问请在评论区留言 [转载请注明出处和署名,谢谢!] 一.java基础 1.多态有哪些体现形式? 重写.重载 2. Overriding的是什么 ...
- 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...
- Java基础之多线程
1.进程和线程: 进程:正在进行的程序.每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元. 线程:进程内部的一条执行路径或者一个控制单元. 两者的区别: 一个进程至少有一个线程 ...
- Java基础入门知识
Java编程入门知识 知识概要: (1)Java入门基本常识 (2)Java的特性跨平台性 (3)Java的编程环境的搭建 (4)Java的运行机制 (5)第一个Java小程序入门 (1)Java ...
- Java基础之多线程详细分析
在了解多线程之前,先来了解一下进程与线程之间的关系. 进程和线程: 进程是指在系统中正在执行的一个程序,每个进程之间是独立的. 线程是进程的一个基本执行单元.一个进程要想执行任务,必须得有线程(每1个 ...
随机推荐
- 大数据技术之_16_Scala学习_07_数据结构(上)-集合
第十章 数据结构(上)-集合10.1 数据结构特点10.1.1 Scala 集合基本介绍10.1.2 可变集合和不可变集合举例10.2 Scala 不可变集合继承层次一览图10.2.1 图10.2.2 ...
- CocoaPods 2017最新、最快安装和使用说明
2017 - 11 - 29 更新 记录: 今天把系统升级到了最新的10.13 也就是high sierra,导入snapKit的时候Cocoapods的时候出现了下面的问题: -bash: /usr ...
- go时间转化
将string转化为time.Time layout := "2006-01-02 15:04:05" str := "2017-11-24 15:10:22" ...
- noip2017集训测试赛(三) Problem B: mex [补档]
Description 给你一个无限长的数组,初始的时候都为0,有3种操作: 操作1是把给定区间[l,r][l,r] 设为1, 操作2是把给定区间[l,r][l,r] 设为0, 操作3把给定区间[l, ...
- PythonGUI编程--向列表框添加滚动条
代码如下: from tkinter import * window = Tk() window.title("New England") yscroll = Scrollbar( ...
- redis基本命令,配置参数
https://www.w3cschool.cn/redis/redis-pub-sub.html redis-server --maxclients 100000 客户端命令CLIENT LIST ...
- BSP
1 BSP概述 BSP即Board Support Package,板级支持包.它来源于嵌入式操作系统与硬件无关的设计思想,操作系统被设计为运行在虚拟的硬件平台上.对于具体的硬件平台,与硬 ...
- Java获取日期属于当年第几周
String today = "2013-01-14"; SimpleDateFormat format = new SimpleDateFormat("yyyy-MM- ...
- 【java】为数组全部元素赋同样的值 以及 数组之间的复制
为数组全部元素赋同样的值 : boolean[] resArray=new boolean[100]; Arrays.fill(resArray, true); 数组之间的复制: System.arr ...
- ACboy needs your help-分组背包模板题
id=17676" target="_blank" style="color:blue; text-decoration:none">ACboy ...