一、複習
二、如何避免僞共享
在JDK8之前是使用填充字節的方式來避免僞共享的,我們最終的目的其實就是希望單個變量能夠獨佔一個緩存行。 我們舉一個類的例子
package com.ruigege.OtherFoundationOfConcurrent2;
public class FiledLong {
public volatile long value =0L;
public long p1,p2,p3,p4,p5,p6;
}
可以看這個類,如果cache行僞64個字節,那麼正好能夠佔滿,七個long類型的變量,其中p1-p6都是用來佔位,還有一個對象的頭佔用八個字節,正好64個字節。 在JDK8中提供了一個註解,用於避免僞共享
@sun.misc.Contended
class FiledLong2{
public volatile long value=0L;
}
用法:既可以用修飾類也可以用來修飾變量。
注意點:@Contended註解只能用於Java的核心類,比如rt包下的類,如果用戶的類需要使用這個註解的時候,需要添加JVM的參數:-XX:-RestrictContended,填充的默認寬度爲128,要自定義寬度可以設置-XX:ContendedPaddingWidth參數
三、出現僞共享內存的條件
在多線程下訪問同一個緩存行的多個變量,纔會出現僞共享變量的問題,如果在單線程下訪問多個變量反而會加速訪問。
四、樂觀鎖和悲觀鎖
1.悲觀鎖
定義:悲觀鎖認爲外界對數據的修改持保守態度,認爲數據很容易被外界修改,在數據被處理之前必須先加鎖,這種鎖是排他鎖,一個線程獲取了鎖之後,其他線程只能等待或者拋出異常。獲取鎖的線程,對記錄進行操作,然後提交事務釋放鎖。下面舉一個例子
//使用悲觀鎖來獲取
EntryObject entry = query("select * from table1 where id =#{id} for update",id);
//修改記錄內容,根據計算修改entry記錄的屬性
String name=generatorName(entry);
entry.setName(name);
//update操作
int count = updateZ("update table1 set name=#{name},age=#{age} where id=#{id}",entry);
return count;
對於上面的代碼,使用了事務切面的方法,只要進入這個方法種就開始執行事物一直到這個方法結束,多個線程調用這個方法的時候,只有一個線程能夠獲取鎖,其他線程就會阻塞掛起,直到原線程釋放鎖 樂觀鎖是相對悲觀鎖而存在的的方式,一般認爲如果只是訪問數據那麼就是可以不用加鎖,只有要更新數據的時候,纔會正式對數據衝突與否進行檢測,具體來說,根據update返回的行數讓用戶決定如何去做。將上面的例子改爲樂觀鎖。
package com.ruigege.OtherFoundationOfConcurrent2;
public class UpdateEntry2 {
public int updateEntry(long id) {
//使用樂觀鎖獲取指定記錄
EntryObject entry = query("select * from table1 where id=#{id}",id);
//
String name = generatorName(entry);
entry.setName(name);
//update操作
int count = update("update table1 set name=#{name},age=#{age},version=${version}+1 where id=#{id} and version =#{version}",entry);
return count;
}
}
對比上面的代碼就可以知道根據version來進行更新數據,更新成功的話,就會給version+1,其他線程進行更新的時候,如果vesion不對的話,那麼就會停止更新。我們也是使用不斷地循環來獲取鎖而解決更新的問題
package com.ruigege.OtherFoundationOfConcurrent2;
public class updateEntry3 {
boolean result = false;
int retryNum = 5;
while(retryNum>0) {
//使用樂觀鎖獲取記錄
EntryObject entry = query("select * from table1 where id=#{id}",id);
String name = generatorName(entry);
entry.setName(name);
//update操作
int count = update("update table1 set name=#{name},age=#{age},version=${version}+1 where id=#{id} and version =#{version}",entry);
//返回的行如果不是0的話說明更新成功了,那麼即刻跳出循環
if(count == 1) {
result = true;
break;
}
retryNum--;
}
return result;
}
樂觀鎖並不會使用數據庫提供的鎖機制,一般在表中添加version字段或者使用業務狀態來實現,樂觀鎖直到提交時才鎖定,所以不會產生任何死鎖。
五、公平鎖和非公平鎖
公平鎖表示線程獲取鎖的順序是按照先到先得的原則,非公平鎖在運行的時候闖入,也就是不一定先到先得。 ReentrantLock提供了公平和非公平鎖。
六 、源碼:
所在包:com.ruigege.OtherFoundationOfConcurrent2 https://github.com/ruigege66/ConcurrentJava
CSDN:https://blog.csdn.net/weixin_44630050 博客園:https://www.cnblogs.com/ruigege0000/ 歡迎關注微信公衆號:傅里葉變換,個人賬號,僅用於技術交流