从Java 5开始,Java提供了功能更加强大的线程同步机制—通过显式定义同步锁对象来实现同步,这种机制下,同步锁有Lock对象充当。
Lock提供了比synchronized方法和synchronized代码块更广泛的锁定操作,Lock可以具有更灵活的结构,差别巨大的属性,支持过个相关的condition对象。
锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
某些锁可能允许对共享资源并发访问,如ReadWriteLock(读写锁),Lock、ReadWriteLock是Java 5提供的两个接口,为Lock提供ReentrantLock(可重入锁)实现类,为ReadWriteLock提供ReentrantReadWriteLock实现类。Java 8新增StampedLock类,多数场景中可以替代ReentrantReadWriteLock。ReentrantReadWriteLock为读写操作提供了三种锁模式:Writing、ReadingOptimistic、Reading。
在实现线程安全的控制中,比较常用的是ReentrantLock(可重入锁)。使用Lock对象显式加锁、释放锁,使用方式如下:
import java.util.concurrent.locks.ReentrantLock;
public class X {
//定义锁对象
private final ReentrantLock lock = new ReentrantLock();
//定义需要保证线程安全的方法
public void m()
{
//加锁
lock.lock();
try
{
//需要保证线程安全的代码
//mehtod body
}
//使用finally来保证释放锁
finally
{
lock.unlock();
}
}
}
以银行取钱为例
package bankmoney;
import java.util.concurrent.locks.ReentrantLock;
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 String getAccountNo() {
return accountNo;
}
public void setAccountNo(String accountNo) {
this.accountNo = accountNo;
}
public double getBalance() {
return 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("取钱失败!余额不足!");
}
}
//使用finally来保证释放锁
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;
}
}
import bankmoney.Account;
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);
}
}
import bankmoney.Account;
import bankmoney.DrawThread;
public class DrawTest {
public static void main(String []args)
{
Account acct =new Account("123456",1000);
new DrawThread("甲",acct, 200).start();
new DrawThread("乙",acct, 800).start();
new DrawThread("丙",acct, 800).start();
}
}
运行结果如下
甲取钱成功!吐出钞票:200.0
余额为:800.0
丙取钱成功!吐出钞票:800.0
余额为:0.0
取钱失败!余额不足!