1. 程式人生 > >《Java併發程式設計從入門到精通》顯示鎖Lock和ReentrantLock

《Java併發程式設計從入門到精通》顯示鎖Lock和ReentrantLock

javaC作者:張振華    購買連結:天貓商城  JD商城  噹噹書店

顯示鎖Lock和ReentrantLock

Lock是一個介面提供了無條件的、可輪詢的、定時的、可中斷的鎖獲取操作,所有加鎖和解鎖的方法都是顯式的。包路徑是:java.util.concurrent.locks.Lock。核心方法是lock(),unlock(),tryLock(),實現類有ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock。

看一下Lock介面有如下方法:

public abstract interface

 Lock

{

public abstract void lock();

public abstract void lockInterruptibly() throws InterruptedException;

public abstract boolean tryLock();

public abstract boolean tryLock(long paramLong , TimeUnit paramTimeUnit) throws InterruptedException;

public abstract void unlock();

public abstract Condition newCondition();

}

對應的解說如下:

void lock();獲取鎖。如果鎖不可用,出於執行緒排程目的,將禁用當前執行緒,並且在獲得鎖之前,該執行緒將一直處於休眠狀態。
void lockInterruptibly() throws InterruptedException;如果當前執行緒未被中斷,則獲取鎖。如果鎖可用,則獲取鎖,並立即返回。如果鎖不可用,出於執行緒排程目的,將禁用當前執行緒,並且在發生以下兩種情況之一以前,該執行緒將一直處於休眠狀態:鎖由當前執行緒獲得;或者其他某個執行緒中斷 當前執行緒,並且支援對鎖獲取的中斷。如果當前執行緒:在進入此方法時已經設定了該執行緒的中斷狀態;或者在獲取鎖時被中斷 ,並且支援對鎖獲取的中斷,則將丟擲  InterruptedException ,並清除當前執行緒的已中斷狀態。
boolean tryLock();僅在呼叫時鎖為空閒狀態才獲取該鎖。如果鎖可用,則獲取鎖,並立即返回值  true 。如果鎖不可用,則此方法將立即返回值  false 。通常對於那些不是必須獲取鎖的操作可能有用。
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;如果鎖在給定的等待時間內空閒,並且當前執行緒未被中斷,則獲取鎖。如果鎖可用,則此方法將立即返回值  true 。如果鎖不可用,出於執行緒排程目的,將禁用當前執行緒,並且在發生以下三種情況之一前,該執行緒將一直處於休眠狀態:
void unlock();釋放鎖。對應於lock()、tryLock()、tryLock(xx)、lockInterruptibly()等操作,如果成功的話應該對應著一個unlock(),這樣可以避免死鎖或者資源浪費。

newCondition() 返回用來與此 Lock 例項一起使用的 Condition 例項。

ReentrantLock是Lock的實現類,是一個互斥的同步器,它具有擴充套件的能力。在競爭條件下,ReentrantLock 的實現要比現在的 synchronized 實現更具有可伸縮性。(有可能在 JVM 的將來版本中改進 synchronized 的競爭效能)這意味著當許多執行緒都競爭相同鎖定時,使用 ReentrantLock 的吞吐量通常要比 synchronized 好。換句話說,當許多執行緒試圖訪問 ReentrantLock 保護的共享資源時,JVM 將花費較少的時間來排程執行緒,而用更多個時間執行執行緒。雖然 ReentrantLock 類有許多優點,但是與同步相比,它有一個主要缺點 — 它可能忘記釋放鎖定。ReentrantLock實在工作中對方法塊加鎖使用頻率最高的。

使用方法如下:

class X {

private final ReentrantLock lock = new ReentrantLock();

// …

public void m() {

lock.lock(); // 獲得鎖

try {

// … 方法體

finally {

lock.unlock();//解鎖

}

}

}
Lock與synchronized 的比較:

1:Lock使用起來比較靈活,但是必須有釋放鎖的動作;

2:Lock必須手動釋放和開啟鎖,synchronized 不需要;

3:Lock只適用與程式碼塊鎖,而synchronized 物件之間的互斥關係;

請注意以下兩種方式的區別:

第一種方式:兩個方法之間的鎖是獨立的。如下:

public class ReentrantLockDemo {

public static void main(String[] args) {

final Count ct = new Count();

for (int i = 0; i < 2; i++) {

new Thread() {

@Override

public void run() {

ct.get();

}

}.start();

}

for (int i = 0; i < 2; i++) {

new Thread() {

@Override

public void run() {

ct.put();

}

}.start();

}

}

}

class Count {

public void get() {

final ReentrantLock lock = new ReentrantLock();

try {

lock.lock(); // 加鎖

System. out.println(Thread.currentThread().getName() + “get begin”);

Thread. sleep(1000L);// 模仿幹活

System. out.println(Thread.currentThread().getName() + “get end”);

lock.unlock(); // 解鎖

catch (InterruptedException e) {

e.printStackTrace();

}

}

public void put() {

final ReentrantLock lock = new ReentrantLock();

try {

lock.lock(); // 加鎖

System. out.println(Thread.currentThread().getName() + “put begin”);

Thread. sleep(1000L);// 模仿幹活

System. out.println(Thread.currentThread().getName() + “put end”);

lock.unlock(); // 解鎖

catch (InterruptedException e) {

e.printStackTrace();

}

}

}

執行結果如下(每次執行結果都是不一樣的,仔細體會一下):

Thread-0get begin

Thread-1get begin

Thread-2put begin

Thread-3put begin

Thread-0get end

Thread-2put end

Thread-3put end

Thread-1get end

第二種方式,兩個方法之間使用相同的鎖。

ReentrantLockDemo 類的內容不變,將Count中的ReentrantLock改成全域性變數,如下所示:

class Count {

final ReentrantLock lock = new ReentrantLock();

public void get() {

try {

lock.lock(); // 加鎖

System. out.println(Thread.currentThread().getName() + “get begin”);

Thread. sleep(1000L);// 模仿幹活

System. out.println(Thread.currentThread().getName() + “get end”);

lock.unlock(); // 解鎖

catch (InterruptedException e) {

e.printStackTrace();

}

}

public void put() {

try {

lock.lock(); // 加鎖

System. out.println(Thread.currentThread().getName() + “put begin”);

Thread. sleep(1000L);// 模仿幹活

System. out.println(Thread.currentThread().getName() + “put end”);

lock.unlock(); // 解鎖

catch (InterruptedException e) {

e.printStackTrace();

}

}

}

執行結果如下(每次執行結果一樣的,仔細體會一下):

Thread-0get begin

Thread-0get end

Thread-1get begin

Thread-1get end

Thread-2put begin

Thread-2put end

Thread-3put begin

Thread-3put end