/** * 可重入內置鎖 * 每一個Java對象均可以用作一個實現同步的鎖,這些鎖被稱爲內置鎖或監視鎖。線程在進入同步代碼塊以前會自動獲取鎖,而且在退出同步代碼塊時 * 會自動釋放鎖。獲取內置鎖的惟一途徑是進入由這個鎖保護的同步代碼塊或方法。 * 「重入」意味着獲取鎖的操做的粒度是「線程」,而不適合調用。 * 同一個線程在調用本類中其它synchronized方法/塊或父類中的synchronized方法/塊時,都不會阻礙該線程地執行,由於互斥鎖時可重入的。 */ public class Deadlock extends Object { private String objID; public Deadlock(String id) { objID = id; } public synchronized void checkOther(Deadlock other) { print("entering checkOther()"); try { Thread.sleep(2000); } catch (InterruptedException e) { } print("in checkOther() - about to" + "invoke 'other.action()'"); other.action(); print("leaving checkOther()"); } public synchronized void action() { print("entering action()"); try { Thread.sleep(500); } catch (InterruptedException e) { } print("leaving action()"); } public void print(String msg) { threadPrint("objID=" + objID + "-" + msg); } public static void threadPrint(String msg) { String threadName = Thread.currentThread().getName(); System.out.println("[Deadlock] threadName= " + threadName + "#" + msg); } public static void main(String[] args) { final Deadlock obj1 = new Deadlock("obj1"); final Deadlock obj2 = new Deadlock("obj2"); Runnable runA = new Runnable() { @Override public void run() { obj1.checkOther(obj2); } }; Thread threadA = new Thread(runA, "threadA"); threadA.start(); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } Runnable runB = new Runnable() { @Override public void run() { obj2.checkOther(obj1); } }; Thread threadB = new Thread(runB, "threadB"); threadB.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } threadPrint("about to interrupt() threadA"); threadA.interrupt(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } threadPrint("about to interrupt() threadB"); threadB.interrupt(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } threadPrint("did that break the deadlock?"); } }
/** * 重入鎖使用java.util.concurrent.locks.ReentrantLock類來實現 * 爲了保證鎖最終必定會被釋放(可能會有一場發生),要把互斥區放在try語句塊內,並在finally語句塊中釋放鎖,尤爲當有return語句時, * return語句必須放在try字句中,以確保unlock()不會過早發生,從而將數據暴露給第二個任務。 * * 有助於提升「鎖」性能的幾點建議: * 一、減小鎖持有時間;減小鎖的持有時間有助於下降鎖衝突的可能性,進而提高系統的併發能力。 * 二、減少鎖粒度 * a)線程安全的HashMap:使用Collections.synchronizedMap()方法包裝HashMap * public static Map m = Collections.synchronizedMap(new HashMap()) * b)對於ConcurrentHashMap,它內部進一步細分了若干小的HashMap,稱之爲段(SEGMENT)。默認狀況下,一個ConcurrentHashMap被 * 進一步細分爲16個段。因爲默認有16個段,所以,若是夠幸運的話,ConcurrentHashMap能夠同時接受16個線程同時插入 * (若是都插入不一樣的段中),從而大大提升其吞吐量;減小鎖粒度會引入一個新的問題,即:當系統須要取得全局鎖時,其消耗的資源會比較多。 * c)所謂減小鎖粒度,就是隻縮小鎖定對象的範圍,從而減小鎖衝突的可能性,進而提升系統的併發能力; * 三、讀寫分離鎖來替換獨佔鎖 * a)使用讀寫鎖ReadWriteLock能夠提升系統的性能。使用讀寫分離鎖來替代獨佔鎖是減小鎖粒度的一種特殊狀況。 * b)減小鎖粒度是經過分割數據結構實現的,那麼,讀寫鎖則是對系統功能點的分割; * c)在讀多寫少的場合,使用讀寫鎖能夠有效提高系統的併發能力; * 四、鎖分離 * * 五、鎖粗化 * 虛擬機在遇到一連串連續地對同一鎖不斷進行請求和釋放的操做時,便會把全部的鎖操做整合成對鎖的一次請求,從而減小對鎖的請求同步次數, * 這個操做叫作鎖的粗化。 * * synchronized和ReetrantLock * 1)互斥同步最主要的問題就是進行線程阻塞和喚醒所帶來的性能問題,於是這種同步又稱爲阻塞同步,它屬於一種悲觀的併發策略,即線程得到的是 * 獨佔鎖。獨佔鎖意味着其它線程只能依靠阻塞來等待線程釋放鎖。而在CPU轉換線程阻塞時會引發線程上下文切換,當有不少線程競爭鎖的時候, * 會引發CPU頻繁的上下文切換致使效率很低。synchronized採用的即是這種併發策略。 * * 2)基於衝突檢測的樂觀併發策略,通俗地將就是先進性操做,若是沒有其它線程爭用共享數據,那操做就成功了,若是共享數據被爭用,產生了衝突, * 那就在進行其它的補償措施(最多見的補償措施就是不斷的重試,直到試成功爲止),這種樂觀的併發策略的許多實現都不須要把線程掛起,所以這種 * 同步被稱爲非阻塞同步。ReetrantLock採用的即是這種併發策略。在樂觀的併發策略中,須要操做和衝突檢測這兩個步驟具有原子性,它靠硬件指令 * 來保證,這裏用的是CAS操做(Compare and Swap)。 * * 3)基本語法上,ReetrantLock與synchronized很類似,它們都具有同樣的線程重入特性,只是代碼寫法上有點區別而已,一個表現爲API層面的互斥鎖(Lock), * 一個表現爲原生語法層面的互斥鎖(synchronized)。 * * 《Java併發編程實踐》一書給出了使用ReetrantLock的最佳時機: * 當你須要如下高級特性時,才應該使用:可定時的、可輪詢的與可中斷的鎖獲取操做,公平隊列,或者非塊結構的鎖。不然,請使用synchronized。 */
public class ReentrantLockTest implements Runnable { public static ReentrantLock lock = new ReentrantLock(); public static int i = 0; @Override public void run() { for (int j = 0; j < 10000000; j++) { lock.lock(); try { i++; } finally { lock.unlock(); } } } public static void main(String[] args) throws InterruptedException { ReentrantLockTest reentrantLockTest = new ReentrantLockTest(); Thread thread1 = new Thread(reentrantLockTest); Thread thread2 = new Thread(reentrantLockTest); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("[ReentrantLockTest] i=" + i); } }