线程同步是为了让多个线程在共享数据时,保持数据的一致性。举个例子,有两个人同时取钱,假设用户账户余额是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)的更多相关文章

  1. java SE学习之线程同步(详细介绍)

           java程序中可以允许存在多个线程,但在处理多线程问题时,必须注意这样一个问题:               当两个或多个线程同时访问同一个变量,并且一些线程需要修改这个变量时,那么这个 ...

  2. java多线程二之线程同步的三种方法

          java多线程的难点是在:处理多个线程同步与并发运行时线程间的通信问题.java在处理线程同步时,常用方法有: 1.synchronized关键字. 2.Lock显示加锁. 3.信号量Se ...

  3. Java多线程编程(4)--线程同步机制

    一.锁 1.锁的概念   线程安全问题的产生是因为多个线程并发访问共享数据造成的,如果能将多个线程对共享数据的并发访问改为串行访问,即一个共享数据同一时刻只能被一个线程访问,就可以避免线程安全问题.锁 ...

  4. Java多线程学习篇——线程的开启

    随着开发项目中业务功能的增加,必然某些功能会涉及到线程以及并发编程的知识点.笔者就在现在的公司接触到了很多软硬件结合和socket通讯的项目了,很多的功能运用到了串口通讯编程,串口通讯编程的安卓端就是 ...

  5. Java多线程与并发——线程同步

    1.多线程共享数据 在多线程的操作中,多个线程有可能同时处理同一个资源,这就是多线程中的共享数据. 2.线程同步 解决数据共享问题,必须使用同步,所谓同步就是指多个线程在同一时间段内只能有一个线程执行 ...

  6. JAVA多线程学习2--线程同步

    一.线程同步介绍 同步:就是协同步调,按照预定的先后顺序执行.比如:你说完我再说. 线程同步:访问同一个共享资源的时候多个线程能够保证数据的安全性.一致性. 二.JAVA中实现线程同步的方法 实现进程 ...

  7. JAVA多线程学习十六 - 同步集合类的应用

    1.引言 在多线程的环境中,如果想要使用容器类,就需要注意所使用的容器类是否是线程安全的.在最早开始,人们一般都在使用同步容器(Vector,HashTable),其基本的原理,就是针对容器的每一个操 ...

  8. JAVA多线程学习十一-线程锁技术

    前面我们讲到了synchronized:那么这节就来将lock的功效. 一.locks相关类 锁相关的类都在包java.util.concurrent.locks下,有以下类和接口: |---Abst ...

  9. Java多线程学习总结--线程概述及创建线程的方式(1)

    在Java开发中,多线程是很常用的,用得好的话,可以提高程序的性能. 首先先来看一下线程和进程的区别: 1,一个应用程序就是一个进程,一个进程中有一个或多个线程.一个进程至少要有一个主线程.线程可以看 ...

随机推荐

  1. Mac下无法拷贝文件到移动硬盘

    Mac下无法拷贝文件到移动硬盘? 是移动硬盘的文件格式的问题. Mac系统无法识别 NTFS 格式的文件. 将移动硬盘格式化为 exFAT 格式的. 别担心,exFAT 格式的硬盘在Windows下也 ...

  2. datagridview中combobox类型的cell选中一个下拉列表之后,立即生效的事件

    public event EventHandler CurrentCellDirtyStateChanged 当单元格的内容已更改,但更改尚未保存时,该单元格将标记为已修改. 此事件通常会在以下情况下 ...

  3. 【转】对Android开发者有益的40条优化建议

    下面是开始Android编程的好方法: 找一些与你想做事情类似的代码 调整它,尝试让它做你像做的事情 经历问题 使用StackOverflow解决问题 对每个你像添加的特征重复上述过程.这种方法能够激 ...

  4. SQL注入实验,PHP连接数据库,Mysql查看binlog,PreparedStatement,mysqli, PDO

    看到有人说了判断能否sql注入的方法: 简单的在参数后边加一个单引号,就可以快速判断是否可以进行SQL注入,这个百试百灵,如果有漏洞的话,一般会报错. 下面内容参考了这两篇文章 http://blog ...

  5. HDU 3951 (博弈) Coin Game

    先考虑两种简单的情况: 如果先手能一次把硬币拿完,即 k >= n ,那么先手胜 如果每次只能拿一个硬币, 即 k = 1 ,那么如果有奇数个硬币先手胜,如果有偶数个硬币后手胜. 剩下的情况就是 ...

  6. [Web前端系列之_Firebug_00_序]

    [因] 以前一直把Firebug当做参考他人网站界面结构的工具,看看css,js等,没有深挖.这段时间在项目组里主要充当前台工作,也有空,就准备把前端给精通点,firebug作为入手点. [参考资料] ...

  7. php使用第三方登录

    目前只做了微博和qq的,前面的去connect.qq.com,open.weibo.com注册的步骤省略 qq和weibo站点都有可以现在的php版本的api,qq的api相对高大上一些. <s ...

  8. 【英语】Bingo口语笔记(49) - 春节请客吃饭的表达

  9. Heritrix源码分析(六) Heritrix的文件结构分析(转)

    本博客属原创文章,欢迎转载!转载请务必注明出处:http://guoyunsky.iteye.com/blog/642618      本博客已迁移到本人独立博客: http://www.yun5u. ...

  10. Spring中@Resource与@Autoware

    问题 这其实就是@Autoware与@Resource没有正确的使用,这个错误是因为wmPoiOplogService这个变量装配方式是@Resource,按照@Resource的按名字查找的方式,并 ...