文档章节

并发编程基础四--同步代码块,同步方法,lock,死锁

Credo-Zhao
 Credo-Zhao
发布于 2013/02/07 11:41
字数 1664
阅读 2.5K
收藏 6

#程序员薪资揭榜#你做程序员几年了?月薪多少?发量还在么?>>>

老生常谈的一个问题,就bank的那个例子,同一张卡同时取钱,并发2个,每个取800,但卡里只有1000,不能让2个人同时操作.这里就需要线程同步.比如下面的DEMO.

首先定义一个账户类:

package org.credo.thread.tongbu;

public class Account {
	private String account;
	private double money;
	
	public Account(String account,double money){
		this.account=account;
		this.money=money;
	}
	
	//根据account重写hashcode和equals方法.
	public int hasCode(){
		return account.hashCode();
	}
	public boolean equals(Object obj){
		if(this==obj){
			return true;
		}
		if(obj!=null && obj.getClass()==Account.class){
			Account target=(Account)obj;
			return target.getAccount().equals(account);
		}
		return false;
	}
	
	public String getAccount() {
		return account;
	}
	public void setAccount(String account) {
		this.account = account;
	}
	public double getMoney() {
		return money;
	}
	public void setMoney(double money) {
		this.money = money;
	}
}
再就是业务类:
package org.credo.thread.tongbu;

public class Draw extends Thread{
	
	private Account account;
	private double hopeGetMoney;
	
	public Draw(String name,Account account,double hopeGetMoney){
		super(name);
		this.account=account;
		this.hopeGetMoney=hopeGetMoney;
	}
	
	//当多个线程修改同一个共享数据的时候,将会引发数据问题.
	public void run(){
		if(account.getMoney() >= hopeGetMoney){
			System.out.println(account.getAccount()+"---"+getName()+"取钱成功,取出金额为:"+hopeGetMoney);
			try {
				Thread.sleep(1);
			} catch (Exception e) {
				e.printStackTrace();
			}
			//修改金额
			account.setMoney(account.getMoney()-hopeGetMoney);
			System.out.println("余额为:"+account.getMoney());
		}else{
			System.out.println("余额不足,取款失败!");
		}
	}
}
测试类:
package org.credo.thread.tongbu;

public class NoSynchronizedTest {

	public static void main(String[] args) {
		//create a new account
		Account account=new Account("印度人", 1000);
		new Draw("印度人A", account, 800).start();
		new Draw("印度人B", account, 800).start();
	}
}
结果是:

印度人---印度人B取钱成功,取出金额为:800.0
印度人---印度人A取钱成功,取出金额为:800.0
余额为:200.0
余额为:-600.0

自然是错的,毕竟这不是信用卡.

在这种场景下,account这个账户必然要是一定时间内只能一个人操作的,是需要线程的同步的.

1.同步代码块

如下code:在业务类的run方法总加入synchronized代码块.

public void run(){
		//使用account作为同步监视器,任何线程进入下面同步代码块之前必须先获得对account账户的锁定.
		//其他线程无法获得锁,也就无法修改
		//这种做法符合,  加锁---修改---释放锁   的逻辑.
		synchronized (account) {
			if(account.getMoney() >= hopeGetMoney){
				System.out.println(account.getAccount()+"---"+getName()+"取钱成功,取出金额为:"+hopeGetMoney);
				try {
					Thread.sleep(1);
				} catch (Exception e) {
					e.printStackTrace();
				}
				//修改金额
				account.setMoney(account.getMoney()-hopeGetMoney);
				System.out.println("余额为:"+account.getMoney());
			}else{
				System.out.println("余额不足,取款失败!");
			}
		}
		//结束,释放同步锁.
	}
印度人---印度人A取钱成功,取出金额为:800.0
余额为:200.0
余额不足,取款失败!

为了解决问题,java的处理方法就出现了"同步监视器",使用同步监视器的方法就是上面的同步代码块.语法嘛,就同上.

synchronized(obj){
    //此处代码就是同步代码块
}

括号里的obj就是同步监视器,上面的含义就是,线程开始执行同步代码前,必须先获得对同步监视器的锁定.当完成同步代码块后会释放锁定.
Java允许任何对象充当同步监视器,但实际上,我们必须使用可能被并发访问的贡献资源充当同步监视器.如上面代码的 account.

2.同步方法

与同步代码块对应的,java的多线程的安全支持还提供了同步方法,同步方法就是使用synchronized关键字去修饰某个方法.对于同步方法而言,其同步监视器就是this,就是该对象本身.

用过使用同步方法可以很方便的实现线程安全的类,线程安全的类具有如下特征:

  • 该类的对象可以被多个线程安全的访问
  • 每个线程调用该对象的任意方法都会得到正确的结果.
  • 每个线程调用该对象任意方法后,该对象状态依然保持合理状态.

synchronized关键字可以修饰方法,代码块,但不能修改构造器,属性等.

在Account类中加入下面代码:

public synchronized void process(double hopeGetMoney){
		if(money >= hopeGetMoney){
			System.out.println(Thread.currentThread().getName()+"取钱成功,取出金额为:"+hopeGetMoney);
			try {
				Thread.sleep(1);
			} catch (Exception e) {
				e.printStackTrace();
			}
			//修改金额
			this.money=money-hopeGetMoney;
			System.out.println("余额为:"+money);
		}else{
			System.out.println("余额不足,取款失败!");
		}
	}

修改业务类中的run()方法为:

public void run(){
		account.process(hopeGetMoney);
	}
印度人A取钱成功,取出金额为:800.0
余额为:200.0
余额不足,取款失败!

可变类的线程安全是以降低程序的运行效率为代价的.因此如果严格考虑性能的话,可以提供 单线程环境和多线程环境,提供2个版本,,一个就是线程不安全,一个线程安全,分别在单线程和多线程中使用.

比如StringBuffer是多线程安全的,但性能不好.StringBuilder有好的性能,但线程是不安全的.

3.释放同步监视器的锁定

释放:

  • 1.当前线程的同步方法,同步代码块正常运行结束.
  • 2.当前线程在同步代码块,同步方法中遇到break,return什么的,
  • 3发生异常,错误.
  • 4.程序执行了同步监视器对象的wait()方法,线程暂停并释放同步监视器.

不释放:

  • 1.在期内执行了Thread.sleep,thread.yield方法.不会放开.
  • 2.用了suspend挂起,当然不应该用suspend和resume方法.


从JAVA5开始.有了Lock.

先贴代码,上一章最后的代码,不变,只修改account代码如下:

public class Account {
	private String account;
	private double money;
	private final ReentrantLock lock=new ReentrantLock();
	
	public Account(String account,double money){
		this.account=account;
		this.money=money;
	}
	
	public void process(double hopeGetMoney){
		try {
			lock.lock();
		if(money >= hopeGetMoney){
			System.out.println(Thread.currentThread().getName()+"取钱成功,取出金额为:"+hopeGetMoney);
			try {
				Thread.sleep(1);
			} catch (Exception e) {
				e.printStackTrace();
			}
			//修改金额
			this.money=money-hopeGetMoney;
			System.out.println("余额为:"+money);
		}else{
			System.out.println("余额不足,取款失败!");
		}
		} finally {
			lock.unlock();
		}
	}

	
	//根据account重写hashcode和equals方法.
使用的是lock.输出为:

印度人A取钱成功,取出金额为:800.0
余额为:200.0
余额不足,取款失败!

Lock是控制多个线程对共享资源进行访问的工具.通常,锁提供了对共享资源的独占访问,每次只能有一个线程对lock对象加锁,线程开始访问共享资源之前应获得Lock对象.

同步锁里面需要理解的东西也很多,这里........尼玛快过年了,实在没心情搞了.

===============死锁==================

死锁就是两个线程互相等待对方释放同步监视器时就回发生死锁.Java虚拟机没有检测,也没有采取措施来处理死锁情况,所以多线程编程时要注意这个情况.发生死锁的时候,整个程序没错误没异常,没提示,只是说有线程都处于阻塞状态,无法继续.

© 著作权归作者所有

Credo-Zhao

Credo-Zhao

粉丝 311
博文 170
码字总数 246423
作品 0
朝阳
技术主管
私信 提问
加载中

评论(0)

java并发编程基础——线程同步

线程同步 一、线程安全问题 如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的...

osc_xpu2jn41
2019/03/25
0
0
《疯狂java讲义》学习(44):线程同步

1线程同步 多线程编程是有趣的事情,它很容易突然出现“错误情况”,这是有系统的线程调度具有一定的随机性造成的,不过即使程序偶然出现问题,那也是由于编程不当引起的。当使用多个线程来访...

泛泛之素
03/31
0
0
Java面向对象系列[v1.0.0][线程同步]

线程安全问题 如下代码使用两个线程模拟取钱操作,模拟两个人使用同一个账户并发取钱的过程 提供一个取钱的线程类,该线程类根据执行账户、取钱数量进行取钱操作,取钱的逻辑是当余额不足时无...

Davieyang.D.Y
04/10
0
0
Java基础第十九天总结——多线程2

目录: 一、线程的声明周期 二、线程的同步 三、线程的通信 四、JDK5.0新增线程创建方式 /-------------------------分割线-----------------------/ 一、线程的声明周期 JDK中用Thread.Stat...

osc_bc7dotjc
2019/12/07
0
0
Java多线程(全)学习笔记(中)

四.多线程的同步 以一个取钱列子来分析:(用户登录那些省略) Accout类: /*银行取钱,账户类/ public class Accout { //账户编号 private String accoutNo; //账户余额 private double b...

明舞
2014/07/27
85
0

没有更多内容

加载失败,请刷新页面

加载更多

比较好用的自媒体分发工具是哪个?

正是源于互联网生态的健康发展,从15年开始到现在,自媒体文化产业特别有关注度,其品牌形象鲜明、优秀的转化能力的数不胜数的亮点,故而吸引着数不清的创作者,而为了达到更出彩的播放量,这...

易媒助手
35分钟前
38
0
idea2020.1 pojie

前几天最新版的idea2020.1终于发布了,它有多香相信小伙伴们已经有所耳闻。现在就拿出来分享给小伙伴们。   所需要的的东西放在这里了,同时里面也放了一个idea2020.1,小伙伴可以选择性的...

我喜欢你有用吗__
39分钟前
32
0
从企业微信机器人到小爱同学,用 Serverless 实现生活智能化!

通过定时触发器,可以简单快速地定制一个企业微信机器人。我们可以用它来实现喝水、吃饭提醒等小功能,还能实现定时推送新闻、天气,甚至是监控告警的小功能。 使用企业微信机器人 在企业微信...

腾讯云Serverless
42分钟前
32
0
socket编程基础,从了解到实现

java生下来一开始就是为了计算机之间的通信,因此这篇文章也将开始介绍一下java使用socket进行计算机之间的通信,在上一篇文章中已经对网络通信方面的基础知识进行了总结,这篇文章将通过代码...

虹越云霄
43分钟前
41
0
Spring Enable***功能

spring博大精深,衍生出了两大系列:spring boot 和sping cloud快速业务开发模式。 我们进行具体开发时,经常看到这样以Enable*开头的注解,如图 等等好多例子。。。框架自带的Enable* @En...

董广明
45分钟前
41
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部