Java多线程学习总结--线程同步(2)
线程同步是为了让多个线程在共享数据时,保持数据的一致性。举个例子,有两个人同时取钱,假设用户账户余额是1000,第一个用户取钱800,在第一个用户取钱的同时,第二个用户取钱600。银行规定,用户不允许透支,当余额不足时,应该取钱失败。我们先来看一下,如果线程不同步,会出现什么情况。代码如下:
public class SynchronizeApp { /**
* @param args
*/
public static void main(String[] args) { // 获得账户
Account account = new Account();
account.setCardNo("95559");
account.setBalance(1000);
// 用户1取款800
DrawMoney user1 = new DrawMoney(account, 800);
// 用户1取款600
DrawMoney user2 = new DrawMoney(account, 600);
user1.start();
user2.start();
} } class DrawMoney extends Thread {
private Account account;
private double amount; public DrawMoney(Account account, double amount) {
this.account = account;
this.amount = amount;
} @Override
public void run() {
account.draw(amount);
}
} class Account {
private String cardNo;
private double balance; public String getCardNo() {
return cardNo;
} public void setCardNo(String cardNo) {
this.cardNo = cardNo;
} public double getBalance() {
return balance;
} public void setBalance(double balance) {
this.balance = balance;
} /**
* 用户取款
*
* @param amount
* ,取款数量
*/
public void draw(double amount) { if (amount > balance) {
System.out.println("金额不足!");
} else {
try {
// 模拟取款过程
Thread.sleep(100);
balance = balance - amount;
System.out.println("成功取款" + amount + "元, 最新余额为" + balance);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
}
运行结果如下:
可见,如果没有线程同步,当两个线程同时取款时,就会出现数据错误。第二个线程取款时,读取到的账户余额是1000,所以可以执行取款操作,但是进行实际取款时,账户余额被第一个线程修改,实际余额是200,所以取出600后最新余额是-400,同样第一个用户也出现了数据错误,余额是1000,取款800后却变成了-400。这种情况是不允许的。
线程同步有两种方式,第一种是用synchronized关键字,第二种是用lock对象。
使用synchronized关键字可以对方法和代码块进行同步,使用synchronized关键字对方法进行同步时,将synchronized关键字放在方法返回类型前面,synchronized自动锁定当前对象。上边取款操使用synchronized关键字同步方法的代码如下:
class Account {
private String cardNo;
private double balance; public String getCardNo() {
return cardNo;
} public void setCardNo(String cardNo) {
this.cardNo = cardNo;
} public double getBalance() {
return balance;
} public void setBalance(double balance) {
this.balance = balance;
} /**
* 用户取款
*
* @param amount
* ,取款数量
*/
public synchronized void draw(double amount) { if (amount > balance) {
System.out.println("金额不足!");
} else {
try {
// 模拟取款过程
Thread.sleep(100);
balance = balance - amount;
System.out.println("成功取款" + amount + "元, 最新余额为" + balance);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
}
再次运行程序,得到如下结果:
使用synchronized同步代码块的代码如下:
/**
* 用户取款
*
* @param amount
* ,取款数量
*/
public void draw(double amount) { synchronized (this) {
if (amount > balance) {
System.out.println("金额不足!");
} else {
try {
// 模拟取款过程
Thread.sleep(100);
balance = balance - amount;
System.out.println("成功取款" + amount + "元, 最新余额为" + balance);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
还可以使用同步锁来对代码进行同步。使用同步锁时,先调用Lock对象的lock方法,代码执行完毕后,再调用Lock对象的unlock方法。在lock和unlock之间的代码是同步的,同一时间段内只能有一个线程能访问。为了保证能释放锁,把unlock方法放在finally语句块中是比较安全的,代码如下:
private final ReentrantLock lock = new ReentrantLock(); /**
* 用户取款
*
* @param amount
* ,取款数量
*/
public void draw(double amount) {
lock.lock();
try {
if (amount > balance) {
System.out.println("金额不足!");
} else {
// 模拟取款过程
Thread.sleep(100);
balance = balance - amount;
System.out.println("成功取款" + amount + "元, 最新余额为" + balance);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
运行代码,结果和使用synchronized同步方法的的运行结果一样。
Java多线程学习总结--线程同步(2)的更多相关文章
- java SE学习之线程同步(详细介绍)
java程序中可以允许存在多个线程,但在处理多线程问题时,必须注意这样一个问题: 当两个或多个线程同时访问同一个变量,并且一些线程需要修改这个变量时,那么这个 ...
- java多线程二之线程同步的三种方法
java多线程的难点是在:处理多个线程同步与并发运行时线程间的通信问题.java在处理线程同步时,常用方法有: 1.synchronized关键字. 2.Lock显示加锁. 3.信号量Se ...
- Java多线程编程(4)--线程同步机制
一.锁 1.锁的概念 线程安全问题的产生是因为多个线程并发访问共享数据造成的,如果能将多个线程对共享数据的并发访问改为串行访问,即一个共享数据同一时刻只能被一个线程访问,就可以避免线程安全问题.锁 ...
- Java多线程学习篇——线程的开启
随着开发项目中业务功能的增加,必然某些功能会涉及到线程以及并发编程的知识点.笔者就在现在的公司接触到了很多软硬件结合和socket通讯的项目了,很多的功能运用到了串口通讯编程,串口通讯编程的安卓端就是 ...
- Java多线程与并发——线程同步
1.多线程共享数据 在多线程的操作中,多个线程有可能同时处理同一个资源,这就是多线程中的共享数据. 2.线程同步 解决数据共享问题,必须使用同步,所谓同步就是指多个线程在同一时间段内只能有一个线程执行 ...
- JAVA多线程学习2--线程同步
一.线程同步介绍 同步:就是协同步调,按照预定的先后顺序执行.比如:你说完我再说. 线程同步:访问同一个共享资源的时候多个线程能够保证数据的安全性.一致性. 二.JAVA中实现线程同步的方法 实现进程 ...
- JAVA多线程学习十六 - 同步集合类的应用
1.引言 在多线程的环境中,如果想要使用容器类,就需要注意所使用的容器类是否是线程安全的.在最早开始,人们一般都在使用同步容器(Vector,HashTable),其基本的原理,就是针对容器的每一个操 ...
- JAVA多线程学习十一-线程锁技术
前面我们讲到了synchronized:那么这节就来将lock的功效. 一.locks相关类 锁相关的类都在包java.util.concurrent.locks下,有以下类和接口: |---Abst ...
- Java多线程学习总结--线程概述及创建线程的方式(1)
在Java开发中,多线程是很常用的,用得好的话,可以提高程序的性能. 首先先来看一下线程和进程的区别: 1,一个应用程序就是一个进程,一个进程中有一个或多个线程.一个进程至少要有一个主线程.线程可以看 ...
随机推荐
- hbase 学习笔记二----shell
Hbase 是一个分布式的.面向列的开源数据库,其实现是建立在google 的bigTable 理论之上,并基于hadoop HDFS文件系统. Hbase不同于一般的关系型数据库 ...
- HttpClient Post Form提交文件/二进制数据
HttpClient httpClient = HttpClients.createDefault(); HttpPost httppost = new HttpPost(url); Multipar ...
- leetcode:Palindrome Linked List
Given a singly linked list, determine if it is a palindrome. Follow up:Could you do it in O(n) time ...
- dom4j API使用简介
dom4j API使用简介 功能简介 dom4j是一个Java的XML API,类似于jdom,用来读写XML文件的.dom4j是一个非常非常优秀的Java XML API,具有性能优异.功能强大和极 ...
- ural1221. Malevich Strikes Back!
http://acm.timus.ru/problem.aspx?space=1&num=1221 算是枚举的 题目意思是必须划出这样的 11011 10001 00000 10001 110 ...
- 常见NoSQL的特点分析与比较
3.分类: 3.1(最常见的键值存储) a.临时性的:保存在内存中,可以进行快速的保存和读取操作, 数据有可能丢失 b.永久性:在磁盘上保存数据,可以进行快读的读写(没有内存中快),数据不会丢失 c. ...
- QQ邮箱附件发送
一.先造一个邮件发送的窗体 二.编辑邮件发送的代码(包括附件) ①附件添加对话框 string file; private void button6_Click(object sender, Even ...
- 如何直接在github网站上更新你fork的repo?
玩过github的人一定会在你自己的账号上fork了一些github开源项目.这些开源项目往往更新比较活跃,你今天fork用到你自己的项目中去了,过几个星期这个fork的origin可能有一些bugf ...
- 用Visio画UML顺序图
1.顺序图 顺序图又称为时序图,顾名思义,它着重表现的是对象间消息传递的时间顺序.顺序图描述的对象也是一个用例,即一组行为操作,而它表现的是这组行为的先后关系(纵坐标),以及每个行为是属于哪个对象的( ...
- BZOJ 4415 发牌
线段树就好了啊. 为什么一眼splay啊... 其实splay也能过,但是线段树更方便? #include<iostream> #include<cstdio> #includ ...