開心一笑
【老婆出差,送老婆去車站盼玄,本想送到站里面的贴彼,誰知檢票員不讓進,老婆在里面瞅了瞅我哈哈大笑道:“來呀埃儿,來打我捌髡獭!你進不來蝌箍!”青灼。知道老婆一直是個二貨的我無奈地笑笑,誰知檢票員開口了:你進去吧妓盲!這次給你個特例杂拨!我。悯衬。弹沽。檀夹。。策橘≌ǘ桑】
提出問題
java中關于鎖的幾點總結和建議???
解決問題
前言
下面的內(nèi)容有些概念解釋得有點簡單。但是丽已,我不想解釋一堆枯燥的概念蚌堵。如果有哪些解釋的不是很清楚,請諒解沛婴,然后找度娘吼畏。本文中的很多例子是來自網(wǎng)上的,并非有意抄襲嘁灯。
1.線程安全和非線程安全概念
“非線程安全”的問題存在于“實例變量”中泻蚊,如果是方法內(nèi)部的私有變量,則不存在“非線程安全“的問題丑婿。
2.方法鎖性雄,對象鎖,類鎖概念
方法鎖:synchronized 修飾方法時羹奉。
對象鎖:synchronized 修飾方法或代碼塊秒旋。
類鎖:synchronized 修飾靜態(tài)的方法或代碼塊。
總結:關鍵字加到 static 方法上是給 Class 上鎖诀拭,加到非 static 方法上是給對象上鎖滩褥。class 鎖可以對類的所有實例起作用,即就是在任何時候炫加,只能有一個線程訪問該類的 static 方法哀托。因為 static 方法就是類方法残黑。
3.synchronized需要注意的幾點
- synchronized 取得的鎖都是對象鎖撇吞,而不是代碼浮声。
- synchronized 關鍵字是不能繼承的「陈粒基類的方法 synchronized ay(){} 在繼承類中并不自動是 synchronized ay(){}插勤,而是變成了ay(){}。繼承類需要你顯式的指定它的某個方法為 synchronized 方法革骨。
- 一個線程訪問了 synchronized 同步代碼塊中的代碼农尖,另一個線程不可以訪問該對象的任何同步代碼塊,但可以訪問非同步代碼塊良哲。同步代碼塊鎖定的也是當前對象
4. 鎖優(yōu)化的思路
鎖優(yōu)化的思路和方法總結一下盛卡,有以下幾種。
- 減少鎖持有時間(盡量縮小鎖的范圍)
- 減小鎖粒度
- 鎖分離
- 鎖粗化
- 鎖消除
5.盡量縮小鎖的范圍
我們應該確保我們只在必要的地方加鎖筑凫,將鎖從方法聲明移到方法體中會延遲鎖的加載滑沧,進而降低了鎖競爭的可能性并村。先看下面的實例:
class SynObj {
//方法鎖/或者對象鎖
public synchronized void methodA() {
System.out.println("methodA.....");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void methodB() {
//對代碼塊進行鎖,降低鎖的競爭
synchronized(this) {
System.out.println("methodB.....");
}
}
public void methodC() {
String str = "sss";
//這里鎖的是 str 這個對象滓技,而不是 SynObj 對象
synchronized (str) {
System.out.println("methodC.....");
}
}
}
/**
* Created by Ay on 2017/3/26.
*/
public class AyTest {
public static void main(String[] args) {
final SynObj obj = new SynObj();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
obj.methodA();
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
obj.methodB();
}
});
t2.start();
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
obj.methodC();
}
});
t3.start();
}
}
打印結果:
methodA.....
methodC.....
//methodB會隔一段時間才會打印出來
methodB.....
總結:因為哩牍,一個線程訪問了 synchronized 同步代碼塊中的代碼,另一個線程不可以訪問該對象的任何同步代碼塊令漂,但可以訪問非同步代碼塊膝昆。所有縮小鎖的范圍可以在一定程度上提高代碼性能。
6.減少鎖持有時間
這個例子其實和上面的盡量縮小鎖的范圍意思相差無二叠必,也很簡單外潜,具體看下面例子,就不多說了挠唆。
public synchronized void syncMethod(){
method1();
method2();
method3();
}
public void syncMethod(){
method1();
//減少鎖持有的時間
synchronized(this){
method2();
}
method3();
}
7.減小鎖粒度
加鎖粒度:所謂加鎖粒度就是你要鎖住的范圍是多大。
例如:你在家上衛(wèi)生間嘱吗,你只要鎖住衛(wèi)生間就可以了吧玄组,不需要將整個家都鎖起來不讓家人進門吧,衛(wèi)生間就是你的加鎖粒度谒麦。
合理的加鎖粒度:
其實衛(wèi)生間并不只是用來上廁所的俄讹,還可以洗澡,洗手绕德。這里就涉及到優(yōu)化加鎖粒度的問題患膛。你在衛(wèi)生間里洗澡,其實別人也可以同時去里面洗手耻蛇,只要做到隔離起來就可以踪蹬,如果馬桶,浴缸臣咖,洗漱臺都是隔開相對獨立的跃捣,實際上衛(wèi)生間可以同時給三個人使用,當然三個人做的事兒不能一樣夺蛇。這樣就細化了加鎖粒度疚漆,你在洗澡的時候只要關上浴室的門,別人還是可以進去洗手的刁赦。如果當初設計衛(wèi)生間的時候沒有將不同的功能區(qū)域劃分隔離開娶聘,就不能實現(xiàn)衛(wèi)生間資源的最大化使用。這就是設計架構的重要性甚脉。
Java相關鎖粒度注意點:
- 1.讀寫分開(CopyOnWrite)
- 2.將鎖打散(ConcurrentHashMap分解Segment)
- 3.通過synchronized或Lock來鎖定一段代碼區(qū)域時丸升,除了考慮它們鎖定的對象是什么以外,還需要考慮是否可以將范圍縮小一些牺氨。
- 4.定義多個對象发钝,然后讓不同的方法鎖住不同的對象顿涣。
- 5.拆分子方法,對某個必要的子方法加鎖酝豪;通過鎖塊來隔離部分代碼段涛碑。
- 6.JVM對鎖的優(yōu)化有一個粗粒度的動作,我們自己寫代碼時盡量不依賴于JVM這種優(yōu)化機制孵淘。
- 7.用樂觀替代悲觀蒲障,如同步之前先做某些條件判斷。
8.鎖分離
- 最常見的鎖分離就是讀寫鎖ReadWriteLock瘫证,根據(jù)功能進行分離成讀鎖和寫鎖揉阎,這樣讀讀不互斥,讀寫互斥背捌,寫寫互斥毙籽,即保證了線程安全,又提高了性能毡庆。
還有就是網(wǎng)上一個高手寫的一個例子:
public class Grocery {
private final ArrayList fruits = new ArrayList();
private final ArrayList vegetables = new ArrayList();
//對象鎖坑赡,不好,效率低
public synchronized void addFruit(int index, String fruit) {
fruits.add(index, fruit);
}
//對象鎖么抗,不好毅否,效率低
public synchronized void removeFruit(int index) {
fruits.remove(index);
}
//對象鎖,不好蝇刀,效率低
public synchronized void addVegetable(int index, String vegetable) {
vegetables.add(index, vegetable);
}
//對象鎖螟加,不好,效率低
public synchronized void removeVegetable(int index) {
vegetables.remove(index);
}
}
優(yōu)化后:
public class Grocery {
private final ArrayList fruits = new ArrayList();
private final ArrayList vegetables = new ArrayList();
public void addFruit(int index, String fruit) {
//水果鎖
synchronized(fruits) fruits.add(index, fruit);
}
public void removeFruit(int index) {
//水果鎖
synchronized(fruits) {fruits.remove(index);}
}
public void addVegetable(int index, String vegetable) {
//蔬菜鎖
synchronized(vegetables) vegetables.add(index, vegetable);
}
public void removeVegetable(int index) {
//蔬菜鎖
synchronized(vegetables) vegetables.remove(index);
}
}
9.鎖粗化
通常情況下吞琐,為了保證多線程間的有效并發(fā)捆探,會要求每個線程持有鎖的時間盡量短,即在使用完公共資源后站粟,應該立即釋放鎖徐许。只有這樣,等待在這個鎖上的其他線程才能盡早的獲得資源執(zhí)行任務卒蘸。但是雌隅,凡事都有一個度,如果對同一個鎖不停的進行請求缸沃、同步和釋放恰起,其本身也會消耗系統(tǒng)寶貴的資源,反而不利于性能的優(yōu)化 趾牧。
舉個例子:
public void demoMethod(){
synchronized(lock){
//do sth.
}
//做其他不需要的同步的工作检盼,但能很快執(zhí)行完畢
synchronized(lock){
//do sth.
}
}
這種情況,根據(jù)鎖粗化的思想翘单,應該合并
public void demoMethod(){
//整合成一次鎖請求
synchronized(lock){
//do sth.
//做其他不需要的同步的工作吨枉,但能很快執(zhí)行完畢
}
}
當然這是有前提的蹦渣,前提就是中間的那些不需要同步的工作是很快執(zhí)行完成的。
再舉一個極端的例子:
for(int i=0;i<CIRCLE;i++){
synchronized(lock){
}
}
在一個循環(huán)內(nèi)不同得獲得鎖貌亭。雖然JDK內(nèi)部會對這個代碼做些優(yōu)化柬唯,但是還不如直接寫成
synchronized(lock){
for(int i=0;i<CIRCLE;i++){
}
}
當然如果有需求說,這樣的循環(huán)太久圃庭,需要給其他線程不要等待太久锄奢,那只能寫成上面那種。如果沒有這樣類似的需求剧腻,還是直接寫成下面那種比較好拘央。
10 鎖原則的總結
總結:減少對鎖的等待時間的最有效方法是減少這個鎖所保護的范圍大小。下面是一些準則:
- 減少對任何鎖的請求頻率书在。
- 只鎖定訪問共享數(shù)據(jù)的代碼灰伟,而不是一個組件的所有代碼(這將減少鎖的持有時間)。
- 只鎖定特定的數(shù)據(jù)項或結構儒旬,而不是整個例程栏账。
- 始終將鎖和特定的數(shù)據(jù)項或結構關聯(lián)起來,而不是和例程關聯(lián)义矛。
- 對于大的數(shù)據(jù)結構,為結構的每一元素選擇一個鎖盟萨,而不是為整個結構選擇一個鎖凉翻。
- 當持有一個鎖時,從不執(zhí)行同步 I/O 或者任何其他阻塞活動捻激。
- 如果您對您組件中的同一數(shù)據(jù)有多個訪問制轰,請試著將它們移到一起,以便它們可以包含在一個鎖定 - 解鎖操作中胞谭。
- 避免雙喚醒的情況垃杖。如果您在一個鎖下修改了一些數(shù)據(jù),并且不得不通知某人您做了這件事丈屹,那么在公布喚醒之前請釋放該鎖调俘。
- 如果必須同時持有兩個鎖,那么最后請求那個最忙的鎖旺垒。
讀書感悟
來自村上春樹《1Q84》
- 孤獨一人也沒關系彩库,只要能發(fā)自內(nèi)心地愛著一個人,人生就會有救先蒋。哪怕不能和他生活在一起骇钦。
- 有希望之處定有磨練。
- 我能承受任何痛苦竞漾,只要這種痛苦有意義眯搭。
- 如果能真心愛上一個人窥翩,那么不管對方是何等惡劣,哪怕對方并不愛自己鳞仙,人生也至少不會是地獄寇蚊,就算多少有點黯淡。
- 世上的人大半不會用自己的腦袋思考繁扎,而且越是不思考的人幔荒,越不愿傾聽別人說話。
- 我討厭別人的施舍梳玫,因此要盡可能多地施與于人爹梁。
- 有再多的才能,也未必能換來一頓飽飯提澎;但是有優(yōu)秀的直覺姚垃,就完全衣食無憂了。
經(jīng)典故事
【禪師愛蘭花:有一位金代禪師非常喜愛蘭花盼忌,在平日弘法講經(jīng)之余积糯,花費了許多的時間栽種蘭花。有一天谦纱,他要外出云游一段時間看成,臨行前交待弟子?要好好照顧寺里的蘭花。在這段期間跨嘉,弟子們總是細心照顧蘭花川慌,但有一天在澆水時卻不小心將蘭花架碰倒了,所有的蘭花盆都跌碎了祠乃,蘭花散了滿地梦重。弟子們都因此非常恐慌亮瓷,打算等師父回來后琴拧,向師父賠罪領罰。金代禪師回來了嘱支,聞知此事蚓胸,便召集弟子們,不但沒有責怪除师,反而說道:“我種蘭花赢织,一來是希望用來供佛,二來也是為了美化寺廟環(huán)境馍盟,不是為了生氣而種蘭花的于置。】
大神文章
【1】《java多線程編程核心技術》讀書筆記2.1
【2】Java多線程編程核心技術--第二章
【3】方法鎖,對象鎖八毯,類鎖區(qū)別
【4】java中的synchronized(同步代碼塊和同步方法的區(qū)別)
【5】改善Java中鎖的性能
【6】[高并發(fā)Java 九] 鎖的優(yōu)化和注意事項
【7】并發(fā)性能優(yōu)化 – 降低鎖粒度
【8】鎖粒度
其他
如果有帶給你一絲絲小快樂搓侄,就讓快樂繼續(xù)傳遞下去,歡迎點贊话速、頂讶踪、歡迎留下寶貴的意見、多謝支持泊交!