Java死锁、活锁,悲观锁、乐观锁
1、死鎖與活鎖的區(qū)別,死鎖與饑餓的區(qū)別?
死鎖:是指兩個或兩個以上的進程(或線程)在執(zhí)行過程中,因爭奪資源而造成的一種互相等待的現(xiàn)象,若無外力作用,它們都將無法推進下去。?
產(chǎn)生死鎖的必要條件:?
互斥條件:所謂互斥就是進程在某一時間內(nèi)獨占資源。
請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。?
不剝奪條件:進程已獲得資源,在末使用完之前,不能強行剝奪。??
循環(huán)等待條件:若干進程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系。
活鎖:任務(wù)或者執(zhí)行者沒有被阻塞,由于某些條件沒有滿足,導(dǎo)致一直重復(fù)嘗試,失敗,嘗試,失敗。
活鎖和死鎖的區(qū)別在于,處于活鎖的實體是在不斷的改變狀態(tài),所謂的“活”, 而處于死鎖的實體表現(xiàn)為等待;活鎖有可能自行解開,死鎖則不能。
饑餓:一個或者多個線程因為種種原因無法獲得所需要的資源,導(dǎo)致一直無法執(zhí)行的狀態(tài)。?
Java中導(dǎo)致饑餓的原因:?
高優(yōu)先級線程吞噬所有的低優(yōu)先級線程的CPU時間。?
線程被永久堵塞在一個等待進入同步塊的狀態(tài),因為其他線程總是能在它之前持續(xù)地對該同步塊進行訪問。?
線程在等待一個本身也處于永久等待完成的對象(比如調(diào)用這個對象的wait方法),因為其他線程總是被持續(xù)地獲得喚醒。
2、樂觀鎖和悲觀鎖的理解及如何實現(xiàn),有哪些實現(xiàn)方式?
悲觀鎖:總是假設(shè)最壞的情況,每次去拿數(shù)據(jù)的時候都認為別人會修改,所以每次在拿數(shù)據(jù)的時候都會上鎖,這樣別人想拿這個數(shù)據(jù)就會阻塞直到它拿到鎖。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。再比如Java里面的同步原語synchronized關(guān)鍵字的實現(xiàn)也是悲觀鎖。
樂觀鎖:顧名思義,就是很樂觀,每次去拿數(shù)據(jù)的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數(shù)據(jù),可以使用版本號等機制。樂觀鎖適用于多讀的應(yīng)用類型,這樣可以提高吞吐量,像數(shù)據(jù)庫提供的類似于write_condition機制,其實都是提供的樂觀鎖。在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現(xiàn)方式CAS實現(xiàn)的。
樂觀鎖的實現(xiàn)方式:?
使用版本標識來確定讀到的數(shù)據(jù)與提交時的數(shù)據(jù)是否一致。提交后修改版本標識,不一致時可以采取丟棄和再次嘗試的策略。?
java中的Compare and Swap即CAS ,當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程并不會被掛起,而是被告知這次競爭中失敗,并可以再次嘗試。 CAS 操作中包含三個操作數(shù) —— 需要讀寫的內(nèi)存位置(V)、進行比較的預(yù)期原值(A)和擬寫入的新值(B)。如果內(nèi)存位置V的值與預(yù)期原值A(chǔ)相匹配,那么處理器會自動將該位置值更新為新值B。否則處理器不做任何操作。
CAS缺點:??
ABA問題:?
比如說一個線程one從內(nèi)存位置V中取出A,這時候另一個線程two也從內(nèi)存中取出A,并且two進行了一些操作變成了B,然后two又將V位置的數(shù)據(jù)變成A,這時候線程one進行CAS操作發(fā)現(xiàn)內(nèi)存中仍然是A,然后one操作成功。盡管線程one的CAS操作成功,但可能存在潛藏的問題。從Java1.5開始JDK的atomic包里提供了一個類AtomicStampedReference來解決ABA問題。?
循環(huán)時間長開銷大:?
對于資源競爭嚴重(線程沖突嚴重)的情況,CAS自旋的概率會比較大,從而浪費更多的CPU資源,效率低于synchronized。?
只能保證一個共享變量的原子操作:?
當對一個共享變量執(zhí)行操作時,我們可以使用循環(huán)CAS的方式來保證原子操作,但是對多個共享變量操作時,循環(huán)CAS就無法保證操作的原子性,這個時候就可以用鎖。
3、SynchronizedMap和ConcurrentHashMap有什么區(qū)別?
SynchronizedMap一次鎖住整張表來保證線程安全,所以每次只能有一個線程來訪為map。
ConcurrentHashMap使用分段鎖來保證在多線程下的性能。ConcurrentHashMap中則是一次鎖住一個桶。ConcurrentHashMap默認將hash表分為16個桶,諸如get,put,remove等常用操作只鎖當前需要用到的桶。這樣,原來只能一個線程進入,現(xiàn)在卻能同時有16個寫線程執(zhí)行,并發(fā)性能的提升是顯而易見的。?
另外ConcurrentHashMap使用了一種不同的迭代方式。在這種迭代方式中,當iterator被創(chuàng)建后集合再發(fā)生改變就不再是拋出ConcurrentModificationException,取而代之的是在改變時new新的數(shù)據(jù)從而不影響原有的數(shù)據(jù) ,iterator完成后再將頭指針替換為新的數(shù)據(jù) ,這樣iterator線程可以使用原來老的數(shù)據(jù),而寫線程也可以并發(fā)的完成改變。
總結(jié)
以上是生活随笔為你收集整理的Java死锁、活锁,悲观锁、乐观锁的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gc()两分钟了解JDK8默认垃圾收集器
- 下一篇: Java应用程序与小程序之间有那些差别?