与同步代码块对应的,Java的多线程安全支持还提供了同步方法,同步方法就是使用synchronized关键字来修饰某个方法,则该方法称为同步方法。对于同步方法而言,无需显示的指定同步监视器,同步方法的同步监视器是this,也就是该对象本身。
通过同步方法可以非常方便的将某类变成线程安全的类。为什么要用同步方法?我们知道对于不可变类总是线程安全的,因为它的对象的状态不可改变。但可变对象需要额外的方法来保证其线程安全。例如:在模拟银行取款的用户类就是一个可变类。它的账户余额(假设有这个属性)这个属性就是可变的,当两个线程同时修改用户账户这个类的账户余额属性时,程序就会出现异常。可以把用户账户的取钱方法设置为同步方法来保证线程安全。用同步方法模拟银行取款可建立以下类:【1】用户类【2】取款类【3】测试类。
【1】用户类Account代码如下:
package com.thread.synchronizedMethod;
/**
* 用户类
*
* @author wwb
*
*/
public class Account {
private String accountNo;
private double balance;
public Account() {
}
public synchronized void draw(double drawAmount) {
// 账户余额大于取钱数目
if (balance >= drawAmount) {
// 吐出钞票
System.out.println(Thread.currentThread().getName() + "取钱成功!吐出钞票:"
+ drawAmount);
try {
Thread.sleep(1);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
// 修改余额
balance -= drawAmount;
System.out.println("\t余额为: " + balance);
} else {
System.out.println(Thread.currentThread().getName() + "取钱失败!余额不足!");
}
}
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 double getBalance() {
return this.balance;
}
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;
}
}
【2】取款类DrawThread代码:
package com.thread.synchronizedMethod;
/**
* 取款类
*
* @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.draw(drawAmount);
}
}
【3】测试类TestDraw代码:
package com.thread.synchronizedMethod;
/**
* 测试类
*
* @author wwb
*
*/
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();
}
}
通过同步方法可以非常方便的将某类变成线程安全的类。为什么要用同步方法?我们知道对于不可变类总是线程安全的,因为它的对象的状态不可改变。但可变对象需要额外的方法来保证其线程安全。例如:在模拟银行取款的用户类就是一个可变类。它的账户余额(假设有这个属性)这个属性就是可变的,当两个线程同时修改用户账户这个类的账户余额属性时,程序就会出现异常。可以把用户账户的取钱方法设置为同步方法来保证线程安全。用同步方法模拟银行取款可建立以下类:【1】用户类【2】取款类【3】测试类。
【1】用户类Account代码如下:
package com.thread.synchronizedMethod;
/**
* 用户类
*
* @author wwb
*
*/
public class Account {
private String accountNo;
private double balance;
public Account() {
}
public synchronized void draw(double drawAmount) {
// 账户余额大于取钱数目
if (balance >= drawAmount) {
// 吐出钞票
System.out.println(Thread.currentThread().getName() + "取钱成功!吐出钞票:"
+ drawAmount);
try {
Thread.sleep(1);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
// 修改余额
balance -= drawAmount;
System.out.println("\t余额为: " + balance);
} else {
System.out.println(Thread.currentThread().getName() + "取钱失败!余额不足!");
}
}
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 double getBalance() {
return this.balance;
}
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;
}
}
【2】取款类DrawThread代码:
package com.thread.synchronizedMethod;
/**
* 取款类
*
* @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.draw(drawAmount);
}
}
【3】测试类TestDraw代码:
package com.thread.synchronizedMethod;
/**
* 测试类
*
* @author wwb
*
*/
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();
}
}