Java提供了一種更強(qiáng)大的線程同步機(jī)制——通過(guò)顯示定義同步鎖對(duì)象來(lái)實(shí)現(xiàn)同步,同步鎖由Lock對(duì)象充當(dāng)。
Lock是控制多個(gè)線程對(duì)共享資源進(jìn)行訪問(wèn)的工具。通常,鎖提供了對(duì)共享資源的獨(dú)立訪問(wèn),每次只能有一個(gè)線程對(duì)Lock對(duì)象加鎖,線程開(kāi)始訪問(wèn)共享資源之前應(yīng)先獲得Lock對(duì)象
在實(shí)現(xiàn)線程安全的控制中,比較常用的是ReentrantLock(可重寫(xiě)入鎖)。使用該Lock對(duì)象可以顯示地加鎖,釋放鎖,通常使用ReentrantLock格式如下:
class x{
? private final ReentrantLock lock=new ReentrantLock();
?public void m(){
lock.lock();
try{
//需要保證線程安全的代碼
}finally{
lock.unlock();
}
}
}
通過(guò)使用ReentrantLock的方法修改前文所提到的取錢(qián)問(wèn)題。
package com.eduask.java.thread.sync.lock;
import java.util.concurrent.locks.*;
public class Account
{
// 定義鎖對(duì)象
private final ReentrantLock lock = new ReentrantLock();
// // 封裝賬戶編號(hào)、賬戶余額的兩個(gè)成員變量
private String accountNo;
private double balance;
public Account(){}
// 構(gòu)造器
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;
}
// 提供一個(gè)線程安全draw()方法來(lái)完成取錢(qián)操作
public void draw(double drawAmount)
{
// 加鎖
lock.lock();
try
{
// 賬戶余額大于取錢(qián)數(shù)目
if (balance >= drawAmount)
{
System.out.println(Thread.currentThread().getName()
+ "取錢(qián)成功!吐出鈔票:" + drawAmount);
try
{
Thread.sleep(1);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
// 修改余額
balance -= drawAmount;
System.out.println("\t余額為" + balance);
}
else
{
System.out.println(Thread.currentThread().getName()
+ "取錢(qián)失敗!余額不足!");
}
}
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;
}
}
模仿取錢(qián)的多線程
package com.eduask.java.thread.sync.lock;
public class DrawThread extends Thread
{
// 模擬用戶賬戶
private Account account;
// 當(dāng)前取錢(qián)線程所希望取的錢(qián)數(shù)
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);
}
}
測(cè)試類
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();
}
}
結(jié)果: