在Java多線程編程中,我們經常使用synchronized關鍵字來實現同步,控制多線程對變量的訪問,來避免並發問題。
但是有的時候,synchronized關鍵字會顯得過於沉重,不夠靈活。synchronized
方法或語句的使用提供了對與每個對象相關的隱式監視器鎖的訪問,但卻強制所有鎖獲取和釋放均要出現在一個塊結構中:當獲取了多個鎖時,它們必須以相反的順序釋放,且必須在與所有鎖被獲取時相同的詞法范圍內釋放所有鎖。
這個時候Lock出現。
Lock不是Java中的關鍵字而是 java.util.concurrent.locks 包中的一個接口。下面我們簡單介紹一下Lock接口。
一、Lock接口簡介
Lock
實現提供了比使用 synchronized
方法和語句可獲得的更廣泛的鎖定操作。此實現允許更靈活的結構,可以具有差別很大的屬性,可以支持多個相關的 Condition
對象。
Lock相對於synchronized關鍵字而言更加靈活,你可以自由得選擇我你想要加鎖的地方。當然更高的自由度也帶來更多的責任。
我們通常會在try...catch模塊中使用lock關鍵字,在finally模塊中釋放鎖。下面是示范代碼。
Lock l = ...; l.lock(); try { // access the resource protected by this lock
} finally { l.unlock(); }
鎖的鎖定和釋放如果在不同的模塊時,需要謹慎編碼,確保最后鎖一定能夠得到釋放。
二、Lock接口中定義的方法
1、void lock()
獲取鎖。如果鎖不可用,出於線程調度目的,將禁用當前線程,並且在獲得鎖之前,該線程將一直處於休眠狀態
2、void lockInterruptibly()
如果當前線程未被中斷,則獲取鎖。
3、Condition newCondition()
返回綁定到此 Lock 實例的新 Condition 實例。
4、boolean tryLock()
僅在調用時鎖為空閑狀態才獲取該鎖。如果鎖可用,則獲取鎖,並立即返回值 true
。如果鎖不可用,則此方法將立即返回值 false
。
5、boolean tryLock(long time, TimeUnit unit)
如果鎖在給定的等待時間內空閑,並且當前線程未被中斷,則獲取鎖。
6、void unlock()
釋放鎖。在等待條件前,鎖必須由當前線程保持。調用 Condition.await()
將在等待前以原子方式釋放鎖,並在等待返回前重新獲取鎖。
三、實現類ReentrantLock
Lock接口有三個實現類分別是ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock。后面兩個是內部類。
第一個ReentrantLock,在我們平常使用中更為頻繁。
ReentrantLock是一個可重入的互斥鎖 Lock
,它具有與使用 synchronized
方法和語句所訪問的隱式監視器鎖相同的一些基本行為和語義,但功能更強大。
ReentrantLock的構造方法接受一個可選的公平 參數。當設置為 true
時,在多個線程的爭用下,這些鎖傾向於將訪問權授予等待時間最長的線程。否則此鎖將無法保證任何特定訪問順序。也就是說這里可以設置鎖的類型為公平鎖還是非公平鎖。但是,需要注意的是公平鎖的情況下,也不能完全確保公平,它總是趨向公平的情況。
ReentrantLock類中還定義了Lock接口之外的方法,例如int getHoldCount() 、boolean hasQueuedThreads() 、boolean hasWaiters(Condition condition)等等,這些方法可以查詢當前鎖的狀態。
四、代碼使用
package com.test; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class TestLock { public static void main(String[] args) { Lock lock = new ReentrantLock(); for(int i=0;i<5;i++){ ThreadPrintStar threadPrintStar = new TestLock().new ThreadPrintStar(lock); threadPrintStar.start(); } } /** * 打印星星 * @author LKB * */ class ThreadPrintStar extends Thread{ private Lock lock; public ThreadPrintStar(Lock lock) { // TODO Auto-generated constructor stub this.lock = lock; } public void run(){ lock.lock(); try { for(int i=0;i<5;i++){ if(i%2 == 0) System.out.println("i = " + i + "+++" + Thread.currentThread().getName() + " print: " + "☆☆"); else System.out.println("i = " + i + "+++" + Thread.currentThread().getName() + " print: " + "★★"); } System.out.println(); System.out.println(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); }finally { lock.unlock(); } } } }
下面是程序運行結果,可以看到我們通過Lock實現了同步。
i = 0+++Thread-0 print: ☆☆
i = 1+++Thread-0 print: ★★
i = 2+++Thread-0 print: ☆☆
i = 3+++Thread-0 print: ★★
i = 4+++Thread-0 print: ☆☆
i = 0+++Thread-1 print: ☆☆
i = 1+++Thread-1 print: ★★
i = 2+++Thread-1 print: ☆☆
i = 3+++Thread-1 print: ★★
i = 4+++Thread-1 print: ☆☆
i = 0+++Thread-2 print: ☆☆
i = 1+++Thread-2 print: ★★
i = 2+++Thread-2 print: ☆☆
i = 3+++Thread-2 print: ★★
i = 4+++Thread-2 print: ☆☆
i = 0+++Thread-4 print: ☆☆
i = 1+++Thread-4 print: ★★
i = 2+++Thread-4 print: ☆☆
i = 3+++Thread-4 print: ★★
i = 4+++Thread-4 print: ☆☆
i = 0+++Thread-3 print: ☆☆
i = 1+++Thread-3 print: ★★
i = 2+++Thread-3 print: ☆☆
i = 3+++Thread-3 print: ★★
i = 4+++Thread-3 print: ☆☆