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

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

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

线程与进程的本质差别就是有么有数据共享空间。线程之间能够共享数据。进程不能够

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

因为如今业务流程添加。业务节点也添加。使用业务的人员也同一时候添加。这个时候就不可避免的出现并发问题,多个线程同一时候訪问操作某一个数据单元

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

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

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基础入门-多线程同步浅析-以银行转账为样例的更多相关文章

  1. Java 基础入门随笔(10) JavaSE版——单例设计模式

    设计模式:对问题行之有效的解决方式.其实它是一种思想. 1.单例设计模式. 解决的问题:就是可以保证一个类在内存中的对象唯一性.(单个实例) 使用单例设计模式需求:必须对于多个程序使用同一个配置信息对 ...

  2. Java基础技术多线程与并发面试【笔记】

    Java基础技术多线程与并发 什么是线程死锁? ​死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去,我们就可以称 ...

  3. java 又一次抛出异常 相关处理结果演示样例代码

    java 又一次抛出异常 相关处理结果演示样例代码 package org.rui.ExceptionTest; /** * 又一次抛出异常 * 在某些情况下,我们想又一次掷出刚才产生过的违例,特别是 ...

  4. Java基础之多线程篇(线程创建与终止、互斥、通信、本地变量)

    线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ...

  5. 关于java基础、多线程、JavaWeb基础、数据库、SSM、Springboot技术汇总

    作者 : Stanley 罗昊 本人自行总结,纯手打,有疑问请在评论区留言 [转载请注明出处和署名,谢谢!] 一.java基础 1.多态有哪些体现形式? 重写.重载 2. Overriding的是什么 ...

  6. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  7. Java基础之多线程

    1.进程和线程: 进程:正在进行的程序.每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元. 线程:进程内部的一条执行路径或者一个控制单元. 两者的区别: 一个进程至少有一个线程 ...

  8. Java基础入门知识

    Java编程入门知识   知识概要: (1)Java入门基本常识 (2)Java的特性跨平台性 (3)Java的编程环境的搭建 (4)Java的运行机制 (5)第一个Java小程序入门 (1)Java ...

  9. Java基础之多线程详细分析

    在了解多线程之前,先来了解一下进程与线程之间的关系. 进程和线程: 进程是指在系统中正在执行的一个程序,每个进程之间是独立的. 线程是进程的一个基本执行单元.一个进程要想执行任务,必须得有线程(每1个 ...

随机推荐

  1. DP 贪心【p2134】百日旅行

    Background 重要的不是去哪里,而是和你在一起.--小红 对小明和小红来说,2014年7月29日是一个美好的日子.这一天是他们相识100天的纪念日. (小明:小红,感谢你2场大考时默默的支持, ...

  2. POJ2342 Anniversary party(动态规划)(树形DP)

    Anniversary party Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6635   Accepted: 3827 ...

  3. 【带修莫队】【权值分块】bzoj3196 Tyvj 1730 二逼平衡树

    这题用了三种算法写: 分块+二分:O(n*sqrt(n*log(n)) 函数式权值分块:O(n*sqrt(n)) 带修莫队+权值分块:O(n5/3) 结果……复杂度越高的实际上跑得越快……最后这个竟然 ...

  4. 冒泡排序--注意flag变量的设置

    代码: #include<stdio.h> void BubbleSort(int a[],int n){ int i,j; int temp; ; // 此处flag变量的设置可以提高算 ...

  5. JNI概述

    JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++). JNI 让你在利用强大 Java 平台的同时,使你仍然可以用 ...

  6. 移植Linux2.6.38到Tiny6410_1GNandflash

    首先说一下为什么要下决心移植Linux内核,本来移植完Uboot后我想先把韦东山第二期的驱动教程看完后在试图移植内核,可是当我跟着教程写好LCD的驱动后,去设置编译友善的内核来测试程序时,我稍微改一点 ...

  7. 移动端与PHP服务端接口通信流程设计(基础版)

    针对 --->非开放性平台 --->公司内部产品 接口特点汇总: 1.因为是非开放性的,所以所有的接口都是封闭的,只对公司内部的产品有效: 2.因为是非开放性的,所以OAuth那套协议是行 ...

  8. x-forwarded-for之深度挖掘

    如今利用nginx做负载均衡的实例已经很多了,针对不同的应用场合,还有很多需要注意的地方,本文要说的就是在通过CDN 后到达nginx做负载均衡时请求头中的X-Forwarded-For项到底发生了什 ...

  9. vue-resource的使用中需要注意的坑

    先看一段代码: export default { name: 'app', data() { return { articles: [] } }, created: function() { this ...

  10. Oracle中分页查询语句的写法

    要动态的变化分页查询的条件,比如pageNow 这个变量表示的是当前是第几页, oracle分页有通用写法,假设一页5行 select * from ( select t.*,rownum rn fr ...