码迷,mamicode.com
首页 > 编程语言 > 详细

多线程编程(四)--线程同步

时间:2015-06-28 11:14:58      阅读:181      评论:0      收藏:0      [点我收藏+]

标签:

       当使用多个线程来访问同一个数据时,就容易出现线程安全的问题。例如,银行取钱。当我们去自动取款机取钱时,正好另一个人转账,即多个线程修改同一数据,这时就容易出现线程安全问题。


线程安全

/**
 * 账户类,该类封装了账户编号和余额两个属性
 * @author Emily-T
 *
 */
public class Account {

	//账户编号
	private String accountNo;
	//余额
	private double balance;
	public Account(){}
	
	//构造函数
	public Account(String accountNo,double balance){
		this.accountNo = accountNo;
		this.balance = balance;
	}
	
	//下面两个方法根据accountNo来计算Account的hashCode和判断equals
	public int hashCode(){
		return accountNo.hashCode();
	}
	
	public boolean equals(Object obj){
		if (obj != null && obj.getClass() == Account.class) {
			Account target = (Account) obj;
			return target.getAccountNo().equals(accountNo);
		}
		return false;
	}

	public String getAccountNo() {
		return accountNo;
	}

	public void setAccountNo(String accountNo) {
		this.accountNo = accountNo;
	}

	public double getBalance() {
		return balance;
	}

	public void setBalance(double balance) {
		this.balance = balance;
	}
	
	
	
}

/**
 * 取钱的线程类
 * 
 * @author Emily-T
 *
 */
public class DrawThread extends Thread {

	// 模拟用户账户
	private Account account;

	// 当前取钱线程所希望取的钱数
	private double drawAmount;

	public DrawThread(String name, Account account, double drawAmount) {
		super(name);
		this.account = account;
		this.drawAmount = drawAmount;
	}

	// 当多条线程修改同一个共享数据时,将涉及数据安全问题
	public void run() {
		// 账户余额大于取钱数目
		if (account.getBalance() >= drawAmount) {

			// 吐出钞票
			System.out.println("取钱成功!吐出钞票:" + drawAmount);

//			try {
//				Thread.sleep(1);
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
			// 修改余额
			account.setBalance(account.getBalance() - drawAmount);
			System.out.println("\t余额为:" + account.getBalance());
		} else {
			System.out.println(getName() + "取钱失败!余额不足!");
		}
	}
}
/**
 * 启动两个线程
 * @author Emily-T
 *
 */
public class TestDraw {

	public static void main(String[] args){
		//创建一个账户
		Account acct = new Account("1234567",1000);
		//模拟两个线程对同一个账户取钱
		new DrawThread("甲",acct,800).start();
		new DrawThread("乙",acct,800).start();
	}
}
结果:

        技术分享

      从结果看来,账户余额只有1000,取出了1600元,剩下-200元。出现这种结果是因为run方法的方法体不具有同步安全性,程序中有两条并发线程在修改Account对象。


线程同步


修改如下:加上同步代码块:

// 当多条线程修改同一个共享数据时,将涉及数据安全问题
	public void run() {

		// 使用account作为同步监视器,任何线程进入下面同步代码块之前,必须先获得
		// 对account账户的锁定——其他线程无法获得锁,也就是无法修改它
		// 加锁——修改完成——释放锁

		synchronized (account) {
			// 账户余额大于取钱数目
			if (account.getBalance() >= drawAmount) {

				// 吐出钞票
				System.out.println("取钱成功!吐出钞票:" + drawAmount);

				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				// 修改余额
				account.setBalance(account.getBalance() - drawAmount);
				System.out.println("\t余额为:" + account.getBalance());
			} else {
				System.out.println(getName() + "取钱失败!余额不足!");
			}
		}
	}


结果:

       技术分享

        任何时刻只能有一条线程可以获得对同步监视器的锁定,当同步代码块执行结束后,该线程自然释放了对该同步监视器的锁定。

 

        同步监视器的目的:阻止两条线程对同一个共享资源进行并发访问。因此通常推荐使用可能被并发访问的共享资源充当同步监视器。

 

        可变类的线程安全是以降低程序的运行效率为代价的,为了减少程序安全所带来的负面影响,程序可以采用如下策略:

          1、不要对线程安全类的所有方法都进行同步,只对那些会改变竞争资源的方法进行同步

          2、如果可变类有两种运行环境:单线程和多线程环境,则应该为该可变类提供两种版本:线程不安全版本和线程安全版本。在单线程环境中使用线程不安全版本以保证性能,在多线程环境中使用线程安全版本。

多线程编程(四)--线程同步

标签:

原文地址:http://blog.csdn.net/liutengteng130/article/details/46669813

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!