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个 ...
随机推荐
- Linux环境安装python3
linux 安装Python3 1.python下载 请在终端输入如下命令: cd /home wget http://cdn.npm.taobao.org/dist/python/3.6.5/Pyt ...
- 1353表达式括号匹配(stack)
[题目描述] 假设一个表达式有英文字母(小写).运算符(+,—,*,/)和左右小(圆)括号构成,以“@”作为表达式的结束符.请编写一个程序检查表达式中的左右圆括号是否匹配,若匹配,则返回“YES”:否 ...
- 【kmp算法】hdu4763 Theme Section
kmp中next数组的含义是:next[i]表示对于s[0]~s[i-1]这个前缀而言,最大相等的前后缀的长度是多少.规定next[0]=-1. 迭代for(int i=next[i];i!=-1;i ...
- [CF864F]Cities Excursions
题目大意: 一个$n(n\le3000)$个点的有向图,$q(q\le4\times10^5)$组询问,每次询问$s_i,t_i$之间是否存在一条字典序最小的路径(可以重复经过不为$t_i$的结点). ...
- Scala实战高手****第8课:零基础实战Scala最常用数据结构Map和Tuple及Spark源码鉴赏
本课内容1.Map和Tuple在Spark源码中的鉴赏2.Map和Tuple代码操作实战 ------------------------------------------------------- ...
- hdu4565之矩阵快速幂
So Easy! Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total S ...
- delphi杀进程的两种方式
delphi杀进程的两种方式 uint unit Tlhelp32; 第一种:比较简单,根据标题,找到窗口,再找到进程,杀死进程 procedure KillProgram(WindowTitle : ...
- Vue 单页应用:记事本
若文章中存在内容无法加载的情况,请移步作者其他博客. 简书 CSDN 最近在看 Vue 的时候,别人给我安利了一个国外的小案例,通过 Vue 和 Vuex 来实现一个记事本. 仔细剖析下,发现“麻雀虽 ...
- C语言 printf格式化输出,参数详解
有关输出对齐 int main(int argc, char* argv[]){ char insertTime[20] = {"1234567890"}; double in ...
- 消息队列系列(四):Rabbitmq常用命令行
目录:安装目录下的 ./sbin rabbitmqctl [-n node] [-t timeout] [-q] {command} [command options…] http://www.rab ...