我的家園

          我的家園

          [本文是我對Java Concurrency In Practice C13的歸納和總結. ?轉載請注明作者和出處, ?如有謬誤, 歡迎在評論中指正. ]

          任何java對象都可以用作同步的鎖, 為了便于區分, 將其稱為內置鎖.

          JDK5.0引入了顯式鎖: Lock及其子類(如ReentrantLock, ReadWriteLock等).?

          內置鎖和顯式鎖的區別有:

          ?

          1. 可中斷申請

          如果使用synchronized申請一個內置鎖時鎖被其他線程持有, 那么當前線程將被掛起, 等待鎖重新可用, 而且等待期間無法中斷. 而顯式鎖提供了可中斷申請: ??

          public class InterruptedLock extends Thread {
          	private static Lock lock = new ReentrantLock();
          
          	@Override
          	public void run() {
          		try {
          			// 可中斷申請, 在申請鎖的過程中如果當前線程被中斷, 將拋出InterruptedException異常
          			lock.lockInterruptibly();
          		} catch (InterruptedException e) {
          			System.out.println("interruption happened");
          			return;
          		}
          
          		// 如果運行到這里, 說明已經申請到鎖, 且沒有發生異常
          		try {
          			System.out.println("run is holding the lock");
          		} finally {
          			lock.unlock();
          		}
          	}
          
          	public static void main(String[] args) throws InterruptedException {
          		try {
          			lock.lock();
          			System.out.println("main is holding the lock.");
          			Thread thread = new InterruptedLock();
          			thread.start();
          			// 1s后中斷thread線程, 該線程此時應該阻塞在lockInterruptibly方法上
          			Thread.sleep(1000);
          			// 中斷thread線程將導致其拋出InterruptedException異常.
          			thread.interrupt();
          			Thread.sleep(1000);
          		} finally {
          			lock.unlock();
          		}
          	}
          }?

          ?

          2. 嘗試型申請

          Lock.tryLock和Lock.tryLock(long time, TimeUnit unit)方法用于嘗試獲取鎖. 如果嘗試沒有成功, 則返回false, 否則返回true. 而內置鎖則不提供這種特性, 一旦開始申請內置鎖, 在申請成功之前, 線程無法中斷, 申請也無法取消. Lock的嘗試型申請通常用于實現時間限定的task:

          public boolean transferMoney(Account fromAcct, Account toAcct, DollarAmount amount, long timeout, TimeUnit unit)
          		throws InsufficientFundsException, InterruptedException {
          	long fixedDelay = getFixedDelayComponentNanos(timeout, unit);
          	long randMod = getRandomDelayModulusNanos(timeout, unit);
          	// 截止時間
          	long stopTime = System.nanoTime() + unit.toNanos(timeout);
          
          	while (true) {
          		if (fromAcct.lock.tryLock()) {
          			try {
          				if (toAcct.lock.tryLock()) {
          					try {
          						if (fromAcct.getBalance().compareTo(amount) < 0)
          							throw new InsufficientFundsException();
          						else {
          							fromAcct.debit(amount);
          							toAcct.credit(amount);
          							return true;
          						}
          					} finally {
          						// 成功申請到鎖時才需要釋放鎖
          						toAcct.lock.unlock();
          					}
          				}
          			} finally {
          				// 成功申請到鎖時才需要釋放鎖
          				fromAcct.lock.unlock();
          			}
          		}
          		// 如果已經超過截止時間直接返回false, 說明轉賬沒有成功. 否則進行下次嘗試.
          		if (System.nanoTime() < stopTime)
          			return false;
          		NANOSECONDS.sleep(fixedDelay + rnd.nextLong() % randMod);
          	}
          }

          嘗試型申請也是可中斷的.

          ?

          3. 鎖的釋放

          對于內置鎖, 只要代碼運行到同步代碼塊之外, 就會自動釋放鎖, 開發者無需擔心拋出異常, 方法返回等情況發生時鎖會沒有被釋放的問題. 然而對于顯式鎖, 必須調用unlock方法才能釋放鎖. 此時需要開發者自己處理拋出異常, 方法返回等情況. 通常會在finally代碼塊中進行鎖的釋放, 還需注意只有申請到鎖之后才需要釋放鎖, 釋放未持有的鎖可能會拋出未檢查異常.

          所以使用內置鎖更容易一些, 而顯式鎖則繁瑣很多. 但是顯式鎖釋放方式的繁瑣也帶來一個方便的地方: 鎖的申請和釋放不必在同一個代碼塊中.

          ?

          4. 公平鎖

          通過ReentrantLock(boolean fair)構造函數創建ReentranLock鎖時可以為其指定公平策略, 默認情況下為不公平鎖.

          多個線程申請公平鎖時, 申請時間早的線程優先獲得鎖. 然而不公平鎖則允許插隊, 當某個線程申請鎖時如果鎖恰好可用, 則該線程直接獲得鎖而不用排隊. 比如線程B申請某個不公平鎖時該鎖正在由線程A持有, 線程B將被掛起. 當線程A釋放鎖時, 線程B將從掛起狀態中恢復并打算再次申請(這個過程需要一定時間). 如果此時恰好線程C也來申請鎖, 則不公平策略允許線程C立刻獲得鎖并開始運行. 假設線程C在很短的一段時間之后就釋放了鎖, 那么可能線程B還沒有完成恢復的過程. 這樣一來, 節省了線程C從掛起到恢復所需要的時間, 還沒有耽誤線程B的運行. 所以在鎖競爭激烈時, 不公平策略可以提高程序吞吐量.

          內置鎖采用不公平策略, 而顯式鎖則可以指定是否使用不公平策略.

          ?

          5. 喚醒和等待

          線程可以wait在內置鎖上, 也可以通過調用內置鎖的notify或notifyAll方法喚醒在其上等待的線程. 但是如果有多個線程在內置鎖上wait, 我們無法精確喚醒其中某個特定的線程.

          顯式鎖也可以用于喚醒和等待. 調用Lock.newCondition方法可以獲得Condition對象, 調用Condition.await方法將使得線程等待, 調用Condition.singal或Condition.singalAll方法可以喚醒在該Condition對象上等待的線程. 由于同一個顯式鎖可以派生出多個Condition對象, 因此我們可以實現精確喚醒. 具體的應用請參考我早期的一篇博文:http://coolxing.iteye.com/blog/1236696

          ?

          鎖優化

          JDK5.0加入顯式鎖后, 開發者發現顯式鎖相比內置鎖具有明顯的性能優勢, 再加上顯式鎖的諸多新特性, 很多文章和書籍都推薦使用顯式鎖代替內置鎖. 然而JDK6.0對內置鎖做了大量優化, 顯式鎖已經不具備明顯的性能優勢. 所以如果使用的是JDK6.0及之后的版本, 且沒有使用到顯式鎖提供的新特性, 則沒有必要刻意使用顯式鎖, 原因如下:

          1. 內置鎖是JVM的內置特性, 更容易進行優化.

          2. 監控程序(如thread dump)對內置鎖具有更好的支持.?

          3. 大多數開發者更熟悉內置鎖.

          JDK6.0對內置鎖所做的優化措施可以參見"深入理解java虛擬機"13.3節.






          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 防城港市| 鄄城县| 云梦县| 柯坪县| 板桥市| 洪雅县| 桂平市| 临沭县| 衡东县| 肃宁县| 巴塘县| 镶黄旗| 永寿县| 新密市| 宜城市| 台山市| 宽城| 穆棱市| 万宁市| 河池市| 乳山市| 柯坪县| 渭源县| 永嘉县| 福清市| 岳西县| 宁远县| 锡林郭勒盟| 文水县| 杨浦区| 尚义县| 枣强县| 彭阳县| 昌江| 锦屏县| 彰武县| 阳曲县| 资源县| 交城县| 武隆县| 顺义区|