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

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

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

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

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

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

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

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

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. codeforces Round #440 A Search for Pretty Integers【hash/排序】

    A. Search for Pretty Integers [题目链接]:http://codeforces.com/contest/872/problem/A time limit per test ...

  2. HDU 1427 速算24点【数值型DFS】

    速算24点 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submi ...

  3. sqlplus version

    $ export ORACLE_HOME=/opt/oraClient/11.2.0.4$ $ ./sqlplus -version SQL*Plus: Release 11.2.0.4.0 Prod ...

  4. Python自带的hmac模块

    Python自带的hmac模块实现了标准的Hmac算法 我们首先需要准备待计算的原始消息message,随机key,哈希算法,这里采用MD5,使用hmac的代码如下: import hmac mess ...

  5. sping boot 入门

    http://www.cnblogs.com/ityouknow/p/5662753.html http://blog.csdn.net/lxhjh/article/details/51711148 ...

  6. [HTML/CSS]盒子模型,块级元素和行内元素

    目录 概述 盒子模型 块级元素 行内元素 可变元素 总结 概述 在div+css中,了解块级元素和行内元素还是非常有必要的,比如:对行内元素使用width属性就会失效.虽然自己不是做前端的,但是,在项 ...

  7. lync项目总结

    概述 9月份,由于公司人事变动,摆在自己面前也有两条路可选择,一是选择lync,二是选择sharepoint,由于之前,部门老大已经让我看了大概一个月的有关lync方面的资料(文档,代码,项目实施等) ...

  8. MythXinWCF通用宿主绿色版V1.2发布,及服务启动相关说明

      最新下载地址 更新日志: 1.宿主配置增加了最大连接及队列数.允许数据传输量 2.程序大量优化 3.bug修正 4.增加已服务方式启动 点击服务方式启动后,会将软件注册为服务. 然后软件就会变成一 ...

  9. pytimechart使用

    参考网站:http://pythonhosted.org/pytimechart/userguide.html 安装: sudo apt-get install python-chaco python ...

  10. HTC相关开发所需SDK等工具都在这里了。 【转】

    OpenVR SDK https://github.com/ValveSoftware/openvr  OpenVR SDK是由原本的SteamWorks SDK更新而来,新增对HTC VIVE开发者 ...