Java多線程之StampedLock
這里就JUC包中的StampedLock做相關(guān)介紹

概述
在讀多寫(xiě)少的場(chǎng)景下,非常適合使用ReentrantReadWriteLock讀寫(xiě)鎖。但其也存在一定的弊端,其有可能導(dǎo)致寫(xiě)線程饑餓。為此JDK 8中提供了StampedLock類,其是一個(gè)非公平的讀寫(xiě)鎖。其與ReentrantReadWriteLock相比,不僅提供了傳統(tǒng)意義上的悲觀讀鎖和寫(xiě)鎖,最大的區(qū)別是其還為讀操作提供了樂(lè)觀鎖的方法——即所謂的樂(lè)觀讀鎖。當(dāng)然其也有弊端,無(wú)論是悲觀讀鎖還是寫(xiě)鎖,均不支持條件變量Condition;然后從類名也可以看到其是不可重入鎖。需要注意的是,雖然一個(gè)線程可以多次獲取悲觀讀鎖,但究其原因是因?yàn)楸^讀鎖是共享鎖。實(shí)際實(shí)踐中,可以直接通過(guò)writeLock、readLock等阻塞式 或 tryWriteLock、tryReadLock等非阻塞式的方式獲取鎖,也可通過(guò)ReadLockView讀鎖視圖、WriteLockView寫(xiě)鎖視圖、ReadWriteLockView讀寫(xiě)鎖視圖來(lái)進(jìn)行相應(yīng)鎖的操作
基本實(shí)踐
讀鎖、寫(xiě)鎖
這里就基本的悲觀讀鎖、寫(xiě)鎖的使用進(jìn)行實(shí)踐,示例如下所示
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public?class?StampedLockTest1?{
????private?static?DateTimeFormatter?formatter?=?DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
????private?static?ExecutorService?threadPool?=?Executors.newFixedThreadPool(10);
????private?static?StampedLock?stampedLock?=?new?StampedLock();
????private?static?Integer?count;
????/**
?????*?測(cè)試:?讀鎖為共享鎖
?????*/
????@Test
????public?void?test1()?{
????????System.out.println("\n----------------------?Test?1?----------------------");
????????count?=?100;
????????for(int?i=1;?i<5;?i++)?{
????????????Runnable?runnable?=?new?ReadTask("Task"+i);
????????????threadPool.execute(?runnable?);
????????}
????????//?主線程等待所有任務(wù)執(zhí)行完畢
????????try{?Thread.sleep(?10*1000?);?}?catch?(Exception?e)?{}
????}
????/**
?????*?測(cè)試:?寫(xiě)鎖為獨(dú)占鎖
?????*/
????@Test
????public?void?test2()?{
????????System.out.println("\n----------------------?Test?2?----------------------");
????????count?=?200;
????????for(int?i=1;?i<5;?i++)?{
????????????Runnable?runnable?=?new?WriteTask("Task"+i);
????????????threadPool.execute(?runnable?);
????????}
????????//?主線程等待所有任務(wù)執(zhí)行完畢
????????try{?Thread.sleep(?10*1000?);?}?catch?(Exception?e)?{}
????}
????/**
?????*?測(cè)試:?讀寫(xiě)互斥
?????*/
????@Test
????public?void?test3()?{
????????System.out.println("\n----------------------?Test?3?----------------------");
????????count?=?300;
????????for(int?i=1;?i<9;?i++)?{
????????????Runnable?task?=?null;
????????????Boolean?isReadTask?=?RandomUtils.nextBoolean();
????????????if(?isReadTask?)?{
????????????????task?=?new?ReadTask2("讀任務(wù)?#"+i);
????????????}?else?{
????????????????task?=?new?WriteTask2("寫(xiě)任務(wù)?#"+i);
????????????}
????????????threadPool.execute(?task?);
????????}
????????//?主線程等待所有任務(wù)執(zhí)行完畢
????????try{?Thread.sleep(?20*1000?);?}?catch?(Exception?e)?{}
????}
????/**
?????*?打印信息
?????*?@param?msg
?????*/
????public?static?void?info(String?msg)?{
????????String?time?=?formatter.format(LocalTime.now());
????????String?log?=?"["+time+"]?"?+?msg;
????????System.out.println(log);
????}
????@AllArgsConstructor
????private?static?class?ReadTask?implements?Runnable?{
????????private?String?taskName;
????????@Override
????????public?void?run()?{
????????????Integer?localData?=?null;
????????????long?stamp?=?stampedLock.readLock();
????????????try{
????????????????info(taskName?+?":?成功獲取讀鎖,?stamp:?"?+?stamp);
????????????????localData?=?count;
????????????}?catch?(Exception?e)?{
????????????????info(taskName+"Happen?Exception");
????????????}?finally?{
????????????????info(taskName?+?":?釋放讀鎖,?stamp:?"+stamp+",?localData:?"+localData);
????????????????stampedLock.unlockRead(stamp);
????????????}
????????}
????}
????@AllArgsConstructor
????private?static?class?WriteTask?implements?Runnable?{
????????private?String?taskName;
????????@Override
????????public?void?run()?{
????????????long?stamp?=?stampedLock.writeLock();
????????????try?{
????????????????info(taskName?+?":?成功獲取寫(xiě)鎖,?stamp:?"?+?stamp);
????????????????count++;
????????????}?catch?(Exception?e)?{
????????????????info(taskName+"Happen?Exception");
????????????}?finally?{
????????????????info(taskName?+?":?釋放寫(xiě)鎖,?stamp:?"+stamp+",?count:?"?+?count);
????????????????stampedLock.unlockWrite(stamp);
????????????}
????????}
????}
????@AllArgsConstructor
????private?static?class?ReadTask2?implements?Runnable?{
????????private?String?taskName;
????????@Override
????????public?void?run()?{
????????????Integer?localData?=?null;
????????????Lock?readLock?=?stampedLock.asReadLock();
????????????readLock.lock();
????????????try{
????????????????info(taskName?+?":?成功獲取讀鎖");
????????????????localData?=?count;
????????????}?catch?(Exception?e)?{
????????????????info(taskName+"Happen?Exception");
????????????}?finally?{
????????????????info(taskName?+?":?釋放讀鎖,?localData:?"+localData+"\n");
????????????????readLock.unlock();
????????????}
????????}
????}
????@AllArgsConstructor
????private?static?class?WriteTask2?implements?Runnable?{
????????private?String?taskName;
????????@Override
????????public?void?run()?{
????????????Lock?writeLock?=?stampedLock.asWriteLock();
????????????writeLock.lock();
????????????try?{
????????????????info(taskName?+?":?成功獲取寫(xiě)鎖");
????????????????count++;
????????????}?catch?(Exception?e)?{
????????????????info(taskName+"Happen?Exception");
????????????}?finally?{
????????????????info(taskName?+?":?釋放寫(xiě)鎖,?count:?"?+?count?+"\n");
????????????????writeLock.unlock();
????????????}
????????}
????}
}
測(cè)試結(jié)果如下所示,符合預(yù)期

可以看到StampedLock獲取鎖、釋放鎖都需要相應(yīng)的stamp值。為此也可以通過(guò)相應(yīng)的視圖類進(jìn)行操作,如上述代碼的test3所示。其相應(yīng)測(cè)試結(jié)果所示。可以看到悲觀讀鎖是一個(gè)共享鎖,而寫(xiě)鎖則是一個(gè)互斥鎖

樂(lè)觀讀鎖
可通過(guò)tryOptimisticRead獲取一個(gè)stamp,即所謂的樂(lè)觀讀鎖。然后在完成讀操作后,通過(guò)validate方法對(duì)stamp進(jìn)行檢查。由于讀過(guò)程通常是非原子性的,故需要判斷是否存在其他線程在此期間獲取到了寫(xiě)鎖,對(duì)數(shù)據(jù)進(jìn)行了修改。造成當(dāng)前線程讀取的數(shù)據(jù)狀態(tài)不一致(部分為修改前的,部分為修改后的)。如果在當(dāng)前線程進(jìn)行讀的過(guò)程中發(fā)生了修改更新,則檢查結(jié)果為false。這時(shí)再獲取悲觀讀鎖進(jìn)行重讀。事實(shí)上,由于樂(lè)觀讀鎖并沒(méi)有鎖。故其一方面不會(huì)阻塞寫(xiě)線程獲取寫(xiě)鎖,也不需要在結(jié)束后釋放該鎖。示例代碼如下所示
public?class?StampedLockTest2?{
????private?static?DateTimeFormatter?formatter?=?DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
????private?static?ExecutorService?threadPool?=?Executors.newFixedThreadPool(10);
????private?static?StampedLock?stampedLock?=?new?StampedLock();
????private?static?Integer?count;
????/**
?????*?測(cè)試:?樂(lè)觀讀鎖
?????*/
????@Test
????public?void?test1()?{
????????count?=?500;
????????threadPool.execute(?new?ReadTask("讀任務(wù)?#1",0)?);
????????threadPool.execute(?new?ReadTask("讀任務(wù)?#2",0)?);
????????threadPool.execute(?new?ReadTask("讀任務(wù)?#33",2*1000)?);
????????threadPool.execute(?new?ReadTask("讀任務(wù)?#44",2*1000)?);
????????try{?Thread.sleep(?1000?);?}?catch?(Exception?e)?{}
????????threadPool.execute(?new?WriteTask("寫(xiě)任務(wù)?#55")?);
????????//?主線程等待所有任務(wù)執(zhí)行完畢
????????try{?Thread.sleep(?20*1000?);?}?catch?(Exception?e)?{}
????}
????/**
?????*?打印信息
?????*?@param?msg
?????*/
????public?static?void?info(String?msg)?{
????????String?time?=?formatter.format(LocalTime.now());
????????String?log?=?"["+time+"]?"?+?msg;
????????System.out.println(log);
????}
????@AllArgsConstructor
????private?static?class?ReadTask?implements?Runnable?{
????????private?String?taskName;
????????private?Integer?sleepTime;
????????@Override
????????public?void?run()?{
????????????long?stamp?=?stampedLock.tryOptimisticRead();
????????????info(taskName?+?":?成功獲取樂(lè)觀讀鎖,?stamp:?"+stamp);
????????????//?讀取數(shù)據(jù)
????????????Integer?localData?=?count;
????????????//?模擬業(yè)務(wù)耗時(shí)
????????????try{?Thread.sleep(sleepTime);?}?catch?(Exception?e)?{}
????????????info(taskName?+?":localData:?"+localData);
????????????//?檢查在獲取樂(lè)觀鎖后,?是否被寫(xiě)鎖獲得過(guò)
????????????if(?!stampedLock.validate(stamp)?)?{
????????????????info(taskName+":?數(shù)據(jù)被其他線程修改需重讀");
????????????????stamp?=?stampedLock.readLock();
????????????????info(taskName?+?":?成功獲取讀鎖,?stamp:?"?+?stamp);
????????????????try{
????????????????????//?模擬業(yè)務(wù)耗時(shí)
????????????????????try{?Thread.sleep(500);?}?catch?(Exception?e)?{}
????????????????}?catch?(Exception?e)?{
????????????????????info(taskName+"Happen?Exception");
????????????????}?finally?{
????????????????????info(taskName?+?":?釋放讀鎖,?stamp:?"+stamp+",?count:?"?+?count+"\n");
????????????????????stampedLock.unlockRead(stamp);
????????????????}
????????????}
????????}
????}
????@AllArgsConstructor
????private?static?class?WriteTask?implements?Runnable?{
????????private?String?taskName;
????????@Override
????????public?void?run()?{
????????????long?stamp?=?stampedLock.writeLock();
????????????try?{
????????????????info(taskName?+?":?成功獲取寫(xiě)鎖,?stamp:?"?+?stamp);
????????????????count++;
????????????}?catch?(Exception?e)?{
????????????????info(taskName+"Happen?Exception");
????????????}?finally?{
????????????????info(taskName?+?":?釋放寫(xiě)鎖,?stamp:?"+stamp+",?count:?"?+?count?+"\n");
????????????????stampedLock.unlockWrite(stamp);
????????????}
????????}
????}
}
測(cè)試結(jié)果如下所示

參考文獻(xiàn)
Java并發(fā)編程之美 翟陸續(xù)、薛賓田著
