首頁  >  文章  >  Java  >  解讀Java8新特性--StampedLock

解讀Java8新特性--StampedLock

零下一度
零下一度原創
2017-06-17 14:15:031407瀏覽

本文從synchronized、Lock到Java8新增的StampedLock進行比較分析,對Java8新特性之StampedLock相關知識感興趣的朋友一起看看吧

Java8就像一個寶藏,一個小的API改進,也足與寫一篇文章,例如同步,一直是多線程並發編程的一個老話題,相信沒有人喜歡同步的代碼,這會降低應用的吞吐量等性能指標,最壞的時候會掛起死機,但是即使這樣你也沒得選擇,因為要確保資訊的正確性。所以這篇文章決定將從synchronized、Lock到Java8新增的StampedLock進行比較分析,相信StampedLock不會讓大家失望。

synchronized

在java5之前,實作同步主要是使用synchronized。它是Java語言的關鍵字,當它用來修飾一個方法或一個程式碼區塊的時候,能夠保證在同一時刻最多只有一個執行緒執行該段程式碼。

有四個不同的同步區塊:

1.實例方法

2.靜態方法

3.實例方法中的同步區塊

4.靜態方法中的同步區塊

大家對此應該不陌生,所以不多講了,以下是程式碼範例


synchronized(this)
// do operation
}

小結:在多線程並發編程中Synchronized一直是元老級角色,很多人都會稱呼它為重量級鎖,但是隨著Java SE1.6對Synchronized進行了各種優化之後,性能上也有所提升。

Lock

它是Java 5在java.util.concurrent.locks新增的一個API。

Lock是一個接口,核心方法是lock(),unlock(),tryLock(),實作類別有ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock;

ReentrantReadWriteLock,ReadWriteLock, ReentrantLock 和synchronized鎖都有相同的記憶體語意。

與synchronized不同的是,Lock完全用Java寫成,在java這個層面是無關JVM實現的。 Lock提供更靈活的鎖機制,很多synchronized 沒有提供的許多特性,例如鎖投票,定時鎖等候和中斷鎖等候,但因為lock是透過程式碼實現的,要保證鎖定一定會被釋放,就必須將unLock( )放到finally{}中

下面是Lock的一個程式碼範例


rwlock.writeLock().lock();
try {
// do operation
} finally {
rwlock.writeLock().unlock();
}

小結:比synchronized更靈活、更具可擴展性的鎖定機制,但不管怎麼說還是synchronized程式碼要更容易書寫些

#StampedLock

##它是java8在java.util.concurrent.locks新增的一個API。


ReentrantReadWriteLock 在沒有任何讀寫鎖時,才可以取得寫入鎖,這可用於實現了悲觀讀取(Pessimistic Reading),即如果執行中進行讀取時,經常可能有另一個執行要寫入的需求,為了保持同步,ReentrantReadWriteLock 的讀取鎖定就可派上用場。


然而,如果讀取執行情況很多,寫入很少的情況下,使用ReentrantReadWriteLock 可能會使寫入執行緒遭遇飢餓(Starvation)問題,也就是寫入執行緒吃吃無法競爭到鎖定而一直處於等待狀態。

StampedLock控制鎖有三種模式(寫,讀,樂觀讀),一個StampedLock狀態是由版本和模式兩個部分組成,鎖獲取方法返回一個數字作為票據stamp,它用相應的鎖狀態表示並控制訪問,數字0表示沒有寫鎖被授權訪問。在讀鎖上分為悲觀鎖和樂觀鎖。


所謂的樂觀讀模式,也就是若讀的操作很多,寫的操作很少的情況下,你可以樂觀地認為,寫入與讀取同時發生幾率很少,因此不悲觀地使用完全的讀取鎖定,程式可以查看讀取資料之後,是否遭到寫入執行的變更,再採取後續的措施(重新讀取變更信息,或者

拋出異常) ,這一個小小改進,可大幅度提高程式的吞吐量! !

下面是java doc提供的StampedLock一個例子


#

class Point {
 private double x, y;
 private final StampedLock sl = new StampedLock();
 void move(double deltaX, double deltaY) { // an exclusively locked method
  long stamp = sl.writeLock();
  try {
  x += deltaX;
  y += deltaY;
  } finally {
  sl.unlockWrite(stamp);
  }
 }
 //下面看看乐观读锁案例
 double distanceFromOrigin() { // A read-only method
  long stamp = sl.tryOptimisticRead(); //获得一个乐观读锁
  double currentX = x, currentY = y; //将两个字段读入本地局部变量
  if (!sl.validate(stamp)) { //检查发出乐观读锁后同时是否有其他写锁发生?
  stamp = sl.readLock(); //如果没有,我们再次获得一个读悲观锁
  try {
   currentX = x; // 将两个字段读入本地局部变量
   currentY = y; // 将两个字段读入本地局部变量
  } finally {
   sl.unlockRead(stamp);
  }
  }
  return Math.sqrt(currentX * currentX + currentY * currentY);
 }
//下面是悲观读锁案例
 void moveIfAtOrigin(double newX, double newY) { // upgrade
  // Could instead start with optimistic, not read mode
  long stamp = sl.readLock();
  try {
  while (x == 0.0 && y == 0.0) { //循环,检查当前状态是否符合
   long ws = sl.tryConvertToWriteLock(stamp); //将读锁转为写锁
   if (ws != 0L) { //这是确认转为写锁是否成功
   stamp = ws; //如果成功 替换票据
   x = newX; //进行状态改变
   y = newY; //进行状态改变
   break;
   }
   else { //如果不能成功转换为写锁
   sl.unlockRead(stamp); //我们显式释放读锁
   stamp = sl.writeLock(); //显式直接进行写锁 然后再通过循环再试
   }
  }
  } finally {
  sl.unlock(stamp); //释放读锁或写锁
  }
 }
 }

小結:

StampedLock要比ReentrantReadWriteLock更廉價,也就是消耗比較小。

StampedLock與ReadWriteLock效能比較

下圖是和ReadWritLock相比,在一個執行緒情況下,是讀速度其4倍左右,寫是1倍。

下圖是六個執行緒情況下,讀取效能是其數十倍,寫入效能也是近10倍左右:

下圖是吞吐量提高:

總結

#1、synchronized是在JVM層面上實現的,不但可以透過一些監控工具監控synchronized的鎖定,而且在程式碼執行時出現異常,JVM會自動釋放鎖定;

2、ReentrantLock、ReentrantReadWriteLock,、StampedLock都是物件層面的鎖定,要保證鎖定一定會被釋放,就必須將unLock()放到finally{}中;

3、StampedLock 對吞吐量有巨大的改進,特別是在讀取線程越來越多的場景下;

# 4.StampedLock有一個複雜的API,對於加鎖操作,很容易誤用其他方法;

#5、當只有少量競爭者的時候,synchronized是一個很好的通用的鎖定實現;

6、當執行緒成長能夠預估,ReentrantLock是一個很好的通用的鎖定實作;

以上是解讀Java8新特性--StampedLock的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn