今天我們來探討一下Java中的鎖機制。前面我們提到,在JDK1.5之前只能通過synchronized關鍵字來實現同步,這個前面我們已經提到是屬於獨占鎖,性能並不高,因此JDK1.5之后開始借助JNI實現了一套高效的鎖實現!
JDK5以后鎖的接口是JUC中的Lock,我們來先看一下他的相關API文檔。
方法摘要 |
|
void |
lock() |
void |
lockInterruptibly() 如果當前線程未被中斷,則獲取鎖。 如果鎖可用,則獲取鎖,並立即返回。 如果鎖不可用,出於線程調度目的,將禁用當前線程,並且在發生以下兩種情況之一以前,該線程將一直處於休眠狀態: l 鎖由當前線程獲得;或者 l 其他某個線程中斷當前線程,並且支持對鎖獲取的中斷。 如果當前線程: l 在進入此方法時已經設置了該線程的中斷狀態;或者 l 在獲取鎖時被中斷,並且支持對鎖獲取的中斷, 則將拋出 InterruptedException,並清除當前線程的已中斷狀態。 |
Condition |
newCondition() 在等待條件前,鎖必須由當前線程保持。調用 Condition.await() 將在等待前以原子方式釋放鎖,並在等待返回前重新獲取鎖。 |
boolean |
tryLock() 如果鎖可用,則獲取鎖,並立即返回值 true。如果鎖不可用,則此方法將立即返回值 false。
此方法的典型使用語句如下:
Lock lock = ...; if (lock.tryLock()) { try { // manipulate protected state } finally { lock.unlock(); } } else { // perform alternative actions } 此用法可確保如果獲取了鎖,則會釋放鎖,如果未獲取鎖,則不會試圖將其釋放。 |
boolean |
tryLock(long time, TimeUnit unit) 如果鎖可用,則此方法將立即返回值 true。如果鎖不可用,出於線程調度目的,將禁用當前線程,並且在發生以下三種情況之一前,該線程將一直處於休眠狀態: l鎖由當前線程獲得;或者 l其他某個線程中斷當前線程,並且支持對鎖獲取的中斷;或者 l已超過指定的等待時間
如果獲得了鎖,則返回值 true。
如果當前線程: l在進入此方法時已經設置了該線程的中斷狀態;或者 l在獲取鎖時被中斷,並且支持對鎖獲取的中斷, 則將拋出 InterruptedException,並會清除當前線程的已中斷狀態。 如果超過了指定的等待時間,則將返回值 false。如果 time 小於等於0,該方法將完全不等待。 |
void |
unlock() |
相對於API來說,我們並不能看出他到底的優點在哪里?我們來看一個實例
package com.yhj.lock; /** * @Described:原子int類型操作測試用例 * @author YHJ create at 2013-4-26 下午05:58:32 * @ClassNmae com.yhj.lock.AtomicIntegerTestCase */ public interface AtomicIntegerTestCase { /** * ++並返回 * @return * @Author YHJ create at 2013-4-26 下午05:39:47 */ int incrementAndGet(); /** * 取值 * @return * @Author YHJ create at 2013-4-26 下午05:39:56 */ int get(); }
package com.yhj.lock; /** * @Described:帶同步的測試 * @author YHJ create at 2013-4-26 下午05:35:35 * @ClassNmae com.yhj.lock.AtomicIntegerWithLock */ public class AtomicIntegerWithSynchronized implements AtomicIntegerTestCase { private int value; private Object lock = new Object();//為保證兩個方法都在同步內 采用對象同步方法 @Override public int incrementAndGet(){ synchronized(lock){ return ++value; } } @Override public int get(){ synchronized(lock){ return value; } } }
package com.yhj.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @Described:帶鎖的測試 * @author YHJ create at 2013-4-26 下午05:35:35 * @ClassNmae com.yhj.lock.AtomicIntegerWithLock */ public class AtomicIntegerWithLock implements AtomicIntegerTestCase{ private int value; private Lock lock = new ReentrantLock(); @Override public int incrementAndGet(){ try { lock.lock(); return ++value; }finally{ lock.unlock(); } } @Override public int get(){ try { lock.lock(); return value; }finally{ lock.unlock(); } } }
package com.yhj.lock; public class Client { /** * 啟動並等待線程結束 * @param threads * @throws InterruptedException * @Author YHJ create at 2013-4-26 下午05:57:16 */ private static void startAndWait(Thread [] threads) throws InterruptedException{ for(Thread thread:threads) thread.start(); for(Thread thread:threads) thread.join(); } /** * 准備線程數據 * @param threads * @param testCase * @param threadCount * @param loopCount * @Author YHJ create at 2013-4-26 下午06:01:25 */ private static void prepare(Thread [] threads,final AtomicIntegerTestCase testCase,int threadCount,final int loopCount){ for (int i = 0; i < threadCount; i++) { threads[i] = new Thread(){ @Override public void run() { for(int i=0;i<loopCount;++i){ testCase.incrementAndGet(); } } }; } } public static void main(String[] args) throws InterruptedException { //前期數據准備 final int threadCount = 200;//線程數目 final int loopCount = 100000;//循環次數 final AtomicIntegerWithLock lockTestCase = new AtomicIntegerWithLock();//測試對象 final AtomicIntegerWithSynchronized synchronizedtestCase = new AtomicIntegerWithSynchronized(); //第一波數據准備 Thread [] threads= new Thread[threadCount]; prepare(threads, lockTestCase, threadCount, loopCount); //第一波啟動 long costTime = 0; long start = System.nanoTime(); startAndWait(threads); long end = System.nanoTime(); costTime = (end-start); //第一波輸出 System.out.println("AtomicIntegerWithLock result:"+lockTestCase.get()+" costTime:"+costTime); System.out.println("=======我是分割線======="); //第二波數據准備 threads= new Thread[threadCount]; prepare(threads, synchronizedtestCase, threadCount, loopCount); //第二波啟動 costTime = 0; start = System.nanoTime(); startAndWait(threads); end = System.nanoTime(); costTime = (end-start); //第二波輸出 System.out.println("AtomicIntegerWithSynchronized result:"+lockTestCase.get()+" costTime:"+costTime); } }
運行結果:
AtomicIntegerWithLock result:20000000 costTime:1192257757
=======我是分割線=======
AtomicIntegerWithSynchronized result:20000000 costTime:3955951264
這個例子很簡單,200個線程,每次執行10w個++操作,很清楚的大家看到,最好的執行結果都是20000000,都能正常的保證數據的一致性,另外我們還能看到一點,就是Lock消耗的時間要比synchronized少,也證明了Lock的性能是要筆synchronized好的!