Lock的api的用法:
我們先看下lock接口中有哪些方法:
public interface Lock {
/**
* 獲取鎖,如果獲取不到就一直等待,與synchronized一樣
*/
void lock();
/**
* 該方法比較特殊,當通過這個方法去獲取鎖時,如果線程正在等待獲取鎖,
* 則這個線程能夠響應中斷,
* 即中斷線程的等待狀態。也就是說,
* 當兩個線程同時通過lock.lockInterruptibly()想獲取某個鎖時,
* 假若此時線程A獲取到了鎖,而線程B只有在等待,
* 那麼對線程B調用threadB.interrupt()方法能夠中斷線程B的等待過程。
*/
void lockInterruptibly() throws InterruptedException;
/**
* 該方法是有返回值的,它表示用來嘗試獲取鎖,如果獲取成功,則返回true,
* 如果獲取失敗(即鎖已被其他線程獲取),則返回false,
* 也就說這個方法無論如何都會立即返回。在拿不到鎖時不會一直在那等待。
*/
boolean tryLock();
/**
* 該方法和tryLock()方法是類似的,只不過區別在於這個方法在拿不到鎖時會等待一定的時間,
* 在時間期限之內如果還拿不到鎖,就返回false。
* 如果如果一開始拿到鎖或者在等待期間內拿到了鎖,則返回true
*/
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
/**
* 釋放鎖
*/
void unlock();
/**
*
* Condition是Java提供了來實現等待/通知的類,
* Condition類還提供比wait/notify更豐富的功能,
* Condition對象是由lock對象所創建的。但是同一個鎖可以創建多個Condition的對象,
* 即創建多個對象監視器。這樣的好處就是可以指定喚醒線程。
* notify喚醒的線程是隨機喚醒一個 ,具體得會在後面線程間得通信講 */
Condition newCondition();
}
ReentrantLock,意思是“可重入鎖”,關於可重入鎖,我們在前面synchronized中已經提到過,ReentrantLock簡單得用法我們前面一篇也簡單得用到過。ReentrantLock是實現了Lock接口的類,並且ReentrantLock提供了更多的方法,我們先看下ReentrantLock類繼承關係:
public class ReentrantLock implements Lock, java.io.Serializable
我們可以看到實現了lock,和序列化接口。
那麼就先使用下lock得api.
Lock()和unlock()我們上一章已經用到過,下面我們講另外得方法:
tryLock()用法:
public class Account {
private Lock lock = new ReentrantLock();
public void add(BigDecimal amount , String name) {
//嘗試獲取鎖,獲取不到,直接跳出
if(lock.tryLock()) {
System.out.println(" 開始充值,線程名: " + Thread.currentThread().getName());
try {
Thread.sleep(2000);
System.out.println(" 結束充值,線程名: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}else {
System.out.println("獲取鎖失敗,不進行充值,線程名: " + Thread.currentThread().getName());
}
}
}
然後繼續充值線程類:
public class CzThread extends Thread{
private Account account;
private String accountName;
public CzThread(Account account) {
this.account = account;
}
@Override
public void run() {
account.add(new BigDecimal("100"), accountName);
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
}
主線程類:
public class MainThread {
public static void main(String[] args) throws InterruptedException {
Account account = new Account();
CzThread cz1 = new CzThread(account);
cz1.setName("thread1");
cz1.start();
CzThread cz2 = new CzThread(account);
cz2.setName("thread2");
cz2.start();
}
}
執行結果:
獲取鎖失敗,不進行充值,線程名: thread2
開始充值,線程名: thread1
結束充值,線程名: thread1
我們繼續看下tryLock(long time, TimeUnit unit)用法,稍微修改下Account類:
public class Account {
private Lock lock = new ReentrantLock();
public void add(BigDecimal amount , String name) throws InterruptedException {
/***
* 等待了5s後
*/
if(lock.tryLock(5, TimeUnit.SECONDS)) {
System.out.println(" 開始充值,線程名: " + Thread.currentThread().getName());
try {
Thread.sleep(2000);
System.out.println(" 結束充值,線程名: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}else {
System.out.println("獲取鎖失敗,不進行充值,線程名: " + Thread.currentThread().getName());
}
}
}
充值類也要修改下,因爲需要拋異常:
public class CzThread extends Thread{
private Account account;
private String accountName;
public CzThread(Account account) {
this.account = account;
}
@Override
public void run() {
try {
account.add(new BigDecimal("100"), accountName);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
}
看下執行結果:
開始充值,線程名: thread2
結束充值,線程名: thread2
開始充值,線程名: thread1
結束充值,線程名: thread1
我們看到線程1和2都執行完了,因爲等待了5s得鎖,所以會執行。
lockInterruptibly與Condition,我們會在後面得線程通信講,下一篇我們會解析,我們上面講的得方法得原理。