Java提供了一种更强大的线程同步机制——通过显示定义同步锁对象来实现同步,同步锁由Lock对象充当。
Lock是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独立访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
在实现线程安全的控制中,比较常用的是ReentrantLock(可重写入锁)。使用该Lock对象可以显示地加锁,释放锁,通常使用ReentrantLock格式如下:
class x{
private final ReentrantLock lock=new ReentrantLock();
public void m(){
lock.lock();
try{
//需要保证线程安全的代码
}finally{
lock.unlock();
}
}
}
通过使用ReentrantLock的方法修改前文所提到的取钱问题。
package com.eduask.java.thread.sync.lock;
import java.util.concurrent.locks.*;
public class Account
{
// 定义锁对象
private final ReentrantLock lock = new ReentrantLock();
// // 封装账户编号、账户余额的两个成员变量
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 double getBalance()
{
return this.balance;
}
// 提供一个线程安全draw()方法来完成取钱操作
public void draw(double drawAmount)
{
// 加锁
lock.lock();
try
{
// 账户余额大于取钱数目
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()
+ "取钱失败!余额不足!");
}
}
finally
{
// 修改完成,释放锁
lock.unlock();
}
}
public int hashCode()
{
return accountNo.hashCode();
}
public boolean equals(Object obj)
{
if(this == obj)
return true;
if (obj !=null
&& obj.getClass() == Account.class)
{
Account target = (Account)obj;
return target.getAccountNo().equals(accountNo);
}
return false;
}
}
模仿取钱的多线程
package com.eduask.java.thread.sync.lock;
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);
}
}
测试类
package com.eduask.java.thread.sync.lock;
public class DrawTest
{
public static void main(String[] args)
{
Account acct = new Account("1234567" , 1000);
new DrawThread("A" , acct , 800).start();
new DrawThread("B" , acct , 800).start();
}
}
结果: