Lock是java5提供的一個強大的線程同步機制--通過顯示定義同步鎖對象來實現同步。Lock可以顯示的加鎖、解鎖。每次只能有一個線程對lock對象加鎖。
Lock有ReadLock、WriteLock、ReentrantLock(可重入鎖)
常用的就是ReentrantLock。代碼如下:
代碼邏輯:Account賬戶類,實現取錢的同步方法、DrawThread取錢的線程
Account:
package lock.reentrantlock2; 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; } 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 (obj != null && obj.getClass() == Account.class) { Account target = (Account)obj; return target.getAccountNo().equals(accountNo); } return false; } }
DrawThread:
package lock.reentrantlock2; /** * 調用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); } }
TestDraw:
package lock.reentrantlock2; /** */ 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(); } }
運行結果:
甲取錢成功!吐出鈔票:800.0
余額為: 200.0
乙取錢失敗!余額不足!
使用Lock同步與同步方法很相似,都是“加鎖--修改公共變量--釋放鎖”的模式,代碼很容易看懂。兩個線程對應一個Account對象,保證了兩個線程對應一個lock對象,保證了同一時刻只有一個線程進入臨界區。Lock還包含太容易Lock(),以及試圖獲取可中斷鎖的lockInterruptibly(),獲取超時失效鎖的tryLock(long,TimeUnit)等方法。
ReentrantLock鎖具有可重入性可以對已被加鎖的ReentrantLock鎖再次加鎖,線程每次調用lock()加鎖後,必須顯示的調用unlock來釋放鎖,有幾個lock就對應幾個unlock。還有把unlock放在finally代碼塊中,Lock在發生異常時也是不釋放鎖的,所以在finally中釋放更安全。
只是對Lock簡單說明了下,為下一篇線程通信打基礎。