线程同步--同步代码块_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > 线程同步--同步代码块

线程同步--同步代码块

 2012/3/2 9:59:53  wenbing2610  程序员俱乐部  我要评论(0)
  • 摘要:多线程编程是有趣的事情,当常常容易突然出现“错误的情况”,这是由于系统的线程调度具有一定的随机性。即使是程序运行期间偶尔出现的问题,那也是由于我们的编程不当所引起的。当使用多个线程来访问同一个数据时,非常容易出现线程安全问题。关于线程安全问题,有一个经典的问题:银行取钱问题。银行取钱的基本流程可以分为如下几个步骤:【1】用户输入账户,密码,系统判断用户的账户,密码是否匹配。【2】用户输入取款金额。【3】系统判断账户余额是否大于取款金额。【4】如果余额大于取款金额,取款成功,否则取款失败
  • 标签:代码 线程
         多线程编程是有趣的事情,当常常容易突然出现“错误的情况”,这是由于系统的线程调度具有一定的随机性。即使是程序运行期间偶尔出现的问题,那也是由于我们的编程不当所引起的。当使用多个线程来访问同一个数据时,非常容易出现线程安全问题。
          关于线程安全问题,有一个经典的问题:银行取钱问题。银行取钱的基本流程可以分为如下几个步骤:
          【1】用户输入账户,密码,系统判断用户的账户,密码是否匹配。
          【2】用户输入取款金额。
          【3】系统判断账户余额是否大于取款金额。
          【4】如果余额大于取款金额,取款成功,否则取款失败。
           这个流程看上去没有任何问题。但一旦将这个流程放在多线程并发的场景下,就有可能出现问题,但不是说一定。也许程序运行一百万次都没有出现问题,没有出现问题并不等于没有问题!这就是多线程访问同一数据的时候,数据安全性问题,在Java中主要有三种方法解决线程安全问题:同步代码块,同步方法,同步锁。在这里简要介绍下同步代码块方法。
          同步代码块语法格式如下:
          synchronized(){
         ...
         //此处代码就是同步代码块
}
         在以上语法中synchronized后括号里的obj就是同步监视器,以上代码的含义就是:线程开始执行同步代码块之前,必须先获得对同步监视器的锁定。显然模拟一个取款过程可建立三个类:用户账户,取款类,测试类
           用户类Account代码:
package com.thread.test;

/**
* 用户类
*
* @author wwb
*
*/
public class Account
{
/**
* 账户
*/
private String accountNo;
/**
* 账户余额
*/
private double balance;
public Account(){}
//构造器
public Account(String accountNo , double balance)
{
this.accountNo = accountNo;
this.balance = balance;
}
public void setAccountNo(String accountNo)
{
this.accountNo = accountNo;
}
public String getAccountNo()
{
return this.accountNo;
}

public void setBalance(double balance)
{
this.balance = balance;
}
public double getBalance()
{
return this.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;
}
}
           由于同步监视器的目的就是:阻止两条线程对同一个共享资源进行并发访问。因此通常推荐使用可能被并发访问的共享资源充当同步监视器。对于上面的取钱模拟程序,我们应该考虑使用账户作为同步监视器。因此以下的模拟取款程序代码如下:
             取款类DrawThread代码:

package com.thread.test;

/**
* 取款类
*
* @author wwb
*
*/
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() {
// 使用account作为同步监视器,任何线程进入下面同步代码块之前,
// 必须先获得对account账户的锁定——其他线程无法获得锁,也就无法修改它
// 这种做法符合:加锁-->修改完成-->释放锁 逻辑
synchronized (account) {
// 账户余额大于取钱数目
if (account.getBalance() >= drawAmount) {
// 吐出钞票
System.out.println(getName() + "取钱成功!吐出钞票:" + drawAmount);
try {
Thread.sleep(1);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
// 修改余额
account.setBalance(account.getBalance() - drawAmount);
System.out.println("\t余额为: " + account.getBalance());
} else {
System.out.println(getName() + "取钱失败!余额不足!");
}
}
}
}

                测试类TestDraw代码:

package com.thread.test;

/**
* 测试类 *
* @author wwb
*
*/

public class TestDraw {

/**
* @param args
*/
public static void main(String[] args) {
// 创建一个账户
Account acct = new Account("1234567", 1000);
// 模拟两个线程对同一个账户取钱
new DrawThread("甲", acct, 800).start();
new DrawThread("乙", acct, 800).start();
}
}
上一篇: 定时任务 下一篇: FacilityEntityTest.java
发表评论
用户名: 匿名