實現自定義Lock類
實現自定義Lock類
鎖是Java併發API提供的基本同步機制之一,每次只有一個執行緒可以執行程式碼塊,因此用來保護程式碼的關鍵部分。鎖機制提供如下兩種操作:
- lock():當訪問臨界區時呼叫此操作,如果執行緒正在執行此臨界區,其它執行緒將被阻塞直到鎖得到臨界區訪問許可權時才被喚醒。
- unlock():在臨界區結尾呼叫此方法,允許其它執行緒訪問臨界區。
在Java併發API中,鎖在Lock介面中宣告,且在一些類中實現,例如ReentrantLock類。
本節將通過實現Lock介面的類學習如何實現自定義Lock物件,用來保護臨界區。
準備工作
本範例通過Eclipse開發工具實現。如果使用諸如NetBeans的開發工具,開啟並建立一個新的Java專案。
實現過程
通過如下步驟實現範例:
-
建立名為MyAbstractQueuedSynchronizer的類,繼承AbstractQueuedSynchronizer類:
public class MyAbstractQueuedSynchronizer extends AbstractQueuedSynchronizer{
-
宣告名為state的私有AtomicInteger屬性:
private final AtomicInteger state;
-
實現類建構函式,初始化屬性:
public MyAbstractQueuedSynchronizer() { state=new AtomicInteger(0); }
-
實現tryAcquire()方法,此方法試圖將狀態變數值從0變成1。如果改變則返回true值,否則返回false:
@Override protected boolean tryAcquire(int arg) { return state.compareAndSet(0, 1); }
-
實現tryRelease()方法,此方法試圖將狀態變數值從1變成0。如果改變則返回true值,否則返回false:
@Override protected boolean tryRelease(int arg) { return state.compareAndSet(1, 0); } }
-
建立名為MyLock的類,指定其實現Lock介面:
public class MyLock implements Lock {
-
宣告名為sync的私有AbstractQueuedSynchronizer屬性:
private final MyAbstractQueuedSynchronizer sync;
-
實現類建構函式,用新的MyAbstractQueueSynchronizer物件初始化sync屬性:
public MyLock() { sync=new MyAbstractQueuedSynchronizer(); }
-
實現lock()方法,呼叫sync物件的acquire()方法:
@Override public void lock() { sync.acquire(1); }
-
實現lockInterruptibly()方法,呼叫sync物件的acquireInterruptibly()方法:
@Override public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); }
-
實現tryLock()方法,呼叫sync物件的tryAcquireNanos()方法:
@Override public boolean tryLock() { try { return sync.tryAcquireNanos(1, 1000); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); return false; } }
-
實現tryLock()方法的另一個版本,包含兩個引數:名為time的長整型引數和unit的TimeUnit引數。呼叫sync物件的tryAcquireNanos()方法:
@Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, TimeUnit.NANOSECONDS.convert(time, unit)); }
-
實現unlock()方法, 呼叫sync物件的release()方法:
@Override public void unlock() { sync.release(1); }
-
實現newCondition()方法,建立sync物件內部類的新物件,名為ConditionObject:
@Override public Condition newCondition() { return sync.new ConditionObject(); } }
-
建立名為Task的類,指定其實現Runnable介面:
public class Task implements Runnable{
-
宣告名為lock的私有MyLock屬性:
private final MyLock lock;
-
宣告名為name的私有String屬性:
private final String name;
-
實現類建構函式,初始化屬性:
public Task(String name, MyLock lock){ this.lock=lock; this.name=name; }
-
實現類的run()方法,獲取鎖,設定執行緒休眠2秒鐘,然後釋放鎖物件:
@Override public void run() { lock.lock(); System.out.printf("Task: %s: Take the lock\n",name); try { TimeUnit.SECONDS.sleep(2); System.out.printf("Task: %s: Free the lock\n",name); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }
-
通過建立名為Main的類,新增main()方法,實現本範例主類:
public class Main { public static void main(String[] args) {
-
建立名為lock的MyLock物件:
MyLock lock=new MyLock();
-
建立和執行10個Task任務:
for (int i=0; i<10; i++){ Task task=new Task("Task-"+i,lock); Thread thread=new Thread(task); thread.start(); }
-
使用tryLock()方法試圖得到鎖。等待1秒鐘,如果沒有得到鎖,輸出資訊到控制檯,然後再次試圖:
boolean value; do { try { value=lock.tryLock(1,TimeUnit.SECONDS); if (!value) { System.out.printf("Main: Trying to get the Lock\n"); } } catch (InterruptedException e) { e.printStackTrace(); value=false; } } while (!value);
-
輸出指明得到鎖且釋放鎖的資訊到控制檯:
System.out.printf("Main: Got the lock\n"); lock.unlock();
-
輸出指明程式結束的資訊到控制檯:
System.out.printf("Main: End of the program\n"); } }
工作原理
Java併發API提供用來實現具有鎖或訊號的同步機制的類,稱之為AbstractQueuedSynchronizer,從類名可以看出,這是一個抽象類。它提供控制對臨界區訪問的操作,並管理阻塞的執行緒佇列,等待對臨界區的訪問。這些操作基於兩個抽象方法:
- tryAcquire():在試圖訪問臨界區時呼叫此方法,如果呼叫此方法的執行緒能夠訪問臨界區,它將返回true值,否則返回false。
- tryRelease():在試圖解除訪問臨界區時呼叫此方法,如果呼叫此方法的執行緒能夠解除訪問,它將返回true值,否則返回false。
在這些方法中,需要實現用來控制訪問臨界區的機制。本範例中,實現了MyAbstractQueuedSynchonizer類,此類繼承AbstractQueuedSyncrhonizer類,並且使用AtomicInteger變數實現抽象方法來控制訪問臨界區。如果鎖是空閒的,這個變數儲存值為0,,因此執行緒能夠訪問臨界區。如果鎖是阻塞的話,則值為1,執行緒無法訪問臨界區。
- 還用到AtomicInteger類提供的compareAndSet()方法,此方法試圖更改指定為第一個引數的值,並將值指定為第二個引數。為了實現tryAcquire()方法,試圖將原子變數值從零變成一。同樣地,為了實現tryRelease()方法,試圖將原子變數值從一變成零。
因為AbstractQueuedSynchronizer類的其它實現(例如,通過ReentrantLock使用的實現)作為私有類在內部實現,所以必須實現AtomicInteger類。這是在使用它的類中執行的,所以無權訪問它。
然後實現了MyLock類,此類實現了Lock介面且具有作為屬性的MyQueuedSynchronizer物件。為了實現Lock介面的所有方法,使用了MyQueuedSynchronizer物件的方法。
最後,實現了Task類,此類實現了Runnable介面,且使用MyLock物件獲得臨界區訪問權,此臨界區設定執行緒休眠2秒鐘。main類建立了MyLock物件,然後執行10個共享此鎖的Task物件。main類還使用tryLock()方法試圖獲得鎖訪問權。
當執行本範例時,能過觀察到如何只有一個執行緒能夠訪問臨界區,並且當執行緒結束執行時,下一個執行緒接著訪問臨界區。
也能夠使用自定義的Lock介面輸出介面使用情況的日誌資訊,控制介面鎖定的時間,或者實現高階的同步機制進行控制,例如訪問資源,使其只能在特定時間可用。
擴充套件學習
AbstractQueuedSynchronizer類提供了兩個方法,能夠用來管理鎖的狀態,分別是getState()和setState()方法。這些方法接收和返回包含鎖狀態的整型值,可以使用它們代替AtomicInteger屬性來儲存鎖狀態。
AbstractQueuedLongSynchronizer類是Java併發API提供的另一個實現同步機制的類,與AbstractQueuedSynchronizer類相同,但它使用long屬性儲存執行緒狀態。
更多關注
- 第二章“基礎執行緒同步”中的“鎖同步程式碼塊”小節