一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

java鎖的種類及研究

 孤獨一兵 2016-11-07

Java鎖的種類以及辨析鎖作為并發(fā)共享數(shù)據(jù),保證一致性的工具,在JAVA平臺有多種實現(xiàn)(如 synchronized 和 ReentrantLock等等 ) 。這些已經(jīng)寫好提供的鎖為我們開發(fā)提供了便利,但是鎖的具體性質(zhì)以及類型卻很少被提及。本系列文章將分析JAVA中常見的鎖以及其特性,為大家答疑解惑。

1、自旋鎖

2、自旋鎖的其他種類

3、阻塞鎖

4、可重入鎖

5、讀寫鎖

6、互斥鎖

7、悲觀鎖

8、樂觀鎖

9、公平鎖

10、非公平鎖

11、偏向鎖

12、對象鎖

13、線程鎖

14、鎖粗化

15、輕量級鎖

16、鎖消除

17、鎖膨脹

18、信號量

背景

鎖作為并發(fā)共享數(shù)據(jù),保證一致性的工具,在JAVA平臺有多種實現(xiàn)(如 synchronized 和 ReentrantLock等等 ) 。這些已經(jīng)寫好提供的鎖為我們開發(fā)提供了便利,但是鎖的具體性質(zhì)以及類型卻很少被提及。

自旋鎖

自旋鎖是采用讓當前線程不停地的在循環(huán)體內(nèi)執(zhí)行實現(xiàn)的,當循環(huán)的條件被當前線程改變時其他前程才能進入臨界區(qū)。

自旋鎖流程:獲取自旋鎖時,如果沒有任何線程保持該鎖,那么將立即得到鎖;如果在獲取自旋鎖時鎖已經(jīng)有保持者,那么獲取鎖操作將自旋在那里,直到該自旋鎖的保持者釋放了鎖。

簡單實現(xiàn)原理的代碼如下:

1234567891011121314151617/** * 自旋鎖原理簡單示例*/public class SpinLock { private AtomicReference sign = new AtomicReference<>(); // 獲取鎖 public void lock() { Thread current = Thread.currentThread(); while (!sign.compareAndSet(null, current)) { } } // 釋放鎖 public void unlock() { Thread current = Thread.currentThread(); sign.compareAndSet(current, null); }}

要理解以上代碼,我們要先弄清楚AtomicReference的作用。

AtomicReference:位于java.util.concurrent.atomic包下。從包名就可知道它的大致作用:在并發(fā)環(huán)境中保證引用對象的原子操作。

查看AtomicReference源碼:

12345678910111213141516171819202122232425262728293031323334353637383940414243package java.util.concurrent.atomic;import java.util.function.UnaryOperator;import java.util.function.BinaryOperator;import sun.misc.Unsafe;/** * An object reference that may be updated atomically. See the {@link * java.util.concurrent.atomic} package specification for description * of the properties of atomic variables. * @since 1.5 * @author Doug Lea * @param The type of object referred to by this reference */public class AtomicReference implements java.io.Serializable { private static final long serialVersionUID = -1848883965231344442L; private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicReference.class.getDeclaredField('value')); } catch (Exception ex) { throw new Error(ex); } } private volatile V value; ...(省略) /** * Atomically sets the value to the given updated value * if the current value {@code ==} the expected value. * @param expect the expected value * @param update the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ public final boolean compareAndSet(V expect, V update) { return unsafe.compareAndSwapObject(this, valueOffset, expect, update); } ...(省略)

發(fā)現(xiàn)AtomicReference實現(xiàn)的基本原理是使用volatile關(guān)鍵字和Unsafe類來保證其可見性和原子性。(PS:在此暫不作擴展閱讀Unsafe類)

我們重點關(guān)注AtomicReference.compareAndSet()這個自旋鎖用到的方法。從方法注釋和方式實現(xiàn),可以理解:這個方法的意思就是當當前的值==(注意是雙等號)期望的值(即傳入的第一個參數(shù))時,把當前值更新為新值(即傳入的第二個參數(shù)),并且返回true,否則返回false。

再回過頭,看之前自旋鎖的代碼,就很好理解了。一開始AtomicReference中的值為null,當有線程獲得鎖后,將值更新為該線程。當其他線程進入被鎖的方法時,由于sign.compareAndSet(null, current)始終返回的是false,導(dǎo)致while循環(huán)體一直在運行,知道獲得鎖的線程調(diào)用unlock方法,將當前持有線程重新設(shè)置為null:sign.compareAndSet(current, null)其他線程才可獲得鎖。

阻塞鎖

阻塞鎖,與自旋鎖不同,改變了線程的運行狀態(tài)。阻塞鎖,可以說是讓線程進入阻塞狀態(tài)進行等待,當獲得相應(yīng)的信號(喚醒,時間) 時,才可以進入線程的準備就緒狀態(tài),準備就緒狀態(tài)的所有線程,通過競爭,進入運行狀態(tài)。

阻塞鎖和自旋鎖最大的區(qū)別就在于,當獲取鎖是,如果鎖有持有者,當前線程是進入阻塞狀態(tài),等待當前線程結(jié)束而被喚醒的。

簡單實現(xiàn)原理的代碼如下:

123456789101112131415161718192021222324/** * 阻塞鎖原理簡單示例 * * @author zacard * @since 2016-01-13 22:02 */public class BlockLock { private AtomicReference sign = new AtomicReference<>(); // 獲取鎖 public void lock() { Thread current = Thread.currentThread(); if (!sign.compareAndSet(null, current)) { LockSupport.park(); } } // 釋放鎖 public void unlock() { Thread current = Thread.currentThread(); sign.compareAndSet(null, current); LockSupport.unpark(current); }}

要理解以上代碼,我們要先弄清楚LockSupport的作用。

LockSupport:位于java.util.concurrent.locks包下(又是j.u.c)。同樣,從包名和類名即可知道其作用:提供并發(fā)編程中的鎖支持。

還是先查看下LockSupport的源碼:

1234567public class LockSupport { private LockSupport() {} // Cannot be instantiated. private static void setBlocker(Thread t, Object arg) { // Even though volatile, hotspot doesn't need a write barrier here. UNSAFE.putObject(t, parkBlockerOffset, arg); } ...(省略)

又是sun.misc.Unsafe這個類,在此我們不得不先擴展研究下這個Unsafe類的作用和原理了。

sun.misc.Unsafe:有個稱號叫做魔術(shù)類。因為他能直接操作內(nèi)存等一些復(fù)雜操作。包括直接修改內(nèi)存值,繞過構(gòu)造器,直接調(diào)用類方法等。當然,他主要提供了CAS(compareAndSwap)原子操作而被我們熟知。

查看Unsafe類源碼:

12345678910111213141516171819public final class Unsafe { private static final Unsafe theUnsafe; ...(省略) private Unsafe() { } @CallerSensitive public static Unsafe getUnsafe() { Class var0 = Reflection.getCallerClass(); if(!VM.isSystemDomainLoader(var0.getClassLoader())) { throw new SecurityException('Unsafe'); } else { return theUnsafe; } } ...(省略)

根據(jù)代碼可知:Unsafe是final類,意味著我們不能通過繼承來使用或改變這個類的方法。然后構(gòu)造器是私有的,也不能實例化。但是他自己保存了一個靜態(tài)私有不可改變的實例“theUnsafe”,并且只提供了一個靜態(tài)方法getUnsafe()來獲取這個類的實例。

但是這個getUnsafe方法確有個限制:注意if語句里的判斷,他表示如果不是受信任的類調(diào)用,會直接拋出異常。顯然,我們平常編寫的類都是不受信任的!

但是,我們有反射!既然他已經(jīng)持有了一個實例,就能通過反射強行竊取這個私有的實例。

代碼如下:

123456789public void getUnsafe() { try { Field field = Unsafe.class.getDeclaredField('theUnsafe'); field.setAccessible(true); Unsafe unsafe = (Unsafe) field.get(null); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); }}

Unsafe類的方法基本都是native關(guān)鍵字修飾的,也就是說這些方法都是原生態(tài)方法,方法對應(yīng)的實現(xiàn)不是在當前文件,而是在用其他語言(如C和C++)實現(xiàn)的文件中。這也就是為什么Unsafe能夠直接操作內(nèi)存等一些特權(quán)功能的原因。

回過頭看下LockSupport中park()和uppark()這2個方法的作用。

LockSupport.unpark():

123456789101112131415/** * Makes available the permit for the given thread, if it * was not already available. If the thread was blocked on * {@code park} then it will unblock. Otherwise, its next call * to {@code park} is guaranteed not to block. This operation * is not guaranteed to have any effect at all if the given * thread has not been started. * * @param thread the thread to unpark, or {@code null}, in which case * this operation has no effect */public static void unpark(Thread thread) { if (thread != null) UNSAFE.unpark(thread);}

根據(jù)方法注釋:對于給定線程,將許可證設(shè)置為可用狀態(tài)。如果這個線程是因為調(diào)用park()而處于阻塞狀態(tài),則清除阻塞狀態(tài)。反之,這個線程在下次調(diào)用park()時,將保證不被阻塞。

LockSupport.park():

12345/** * Disables the current thread for thread scheduling purposes unless the * permit is available. * *

If the permit is available then it is consumed and the call returns * immediately; otherwise * the current thread becomes disabled for thread scheduling * purposes and lies dormant until one of three things happens: * *

  • *

  • Some other thread invokes {@link #unpark unpark} with the * current thread as the target; or * *

  • Some other thread {@linkplain Thread#interrupt interrupts} * the current thread; or * *

  • The call spuriously (that is, for no reason) returns. *

* *

This method does not report which of these caused the * method to return. Callers should re-check the conditions which caused * the thread to park in the first place. Callers may also determine, * for example, the interrupt status of the thread upon return. * * @param blocker the synchronization object responsible for this * thread parking * @since 1.6 */ public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(false, 0L); setBlocker(t, null); }

根據(jù)注釋:除非許可證是可用的,不然將當前線程的調(diào)度設(shè)置為不可用。當許可是可用時,方法會立即返回,不會阻塞,反之就會阻塞當前線程直到下面3件事發(fā)生:

o其他線程調(diào)用了unpark(此線程)

o其他線程interrupts(終止)了此線程

o調(diào)用時發(fā)生未知原因的返回

重入鎖

重入鎖,也叫做遞歸鎖,指的是同一線程外層函數(shù)獲得鎖之后 ,內(nèi)層遞歸函數(shù)仍然有獲取該鎖的代碼,但不受影響。在JAVA環(huán)境下ReentrantLock和synchronized都是重入鎖。

測試代碼如下:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950/** * 測試ReentrantLock和synchronized */@Testpublic void testReentrantLock() { // ReentrantLock test for (int i = 0; i < 3; i++) { new Thread(new Runnable() { ReentrantLock lock = new ReentrantLock(); public void get() { lock.lock(); System.out.println('ReentrantLock:' + Thread.currentThread().getId()); set(); lock.unlock(); } public void set() { lock.lock(); System.out.println('ReentrantLock:' + Thread.currentThread().getId()); lock.unlock(); } @Override public void run() { get(); } }).start(); } // synchronized test for (int i = 0; i < 3; i++) { new Thread(new Runnable() { public synchronized void get() { System.out.println('synchronized:' + Thread.currentThread().getId()); set(); } public synchronized void set() { System.out.println('synchronized:' + Thread.currentThread().getId()); } @Override public void run() { get(); } }).start(); }}

2段代碼的輸出一致:都會重復(fù)輸出當前線程id2次。

可重入鎖最大的作用是避免死鎖。以自旋鎖作為例子:

123456789101112131415161718192021222324/** * 自旋鎖原理簡單示例 * * @author zacard * @since 2016-01-13 21:40 */public class SpinLock { private AtomicReference sign = new AtomicReference<>(); // 獲取鎖 public void lock() { Thread current = Thread.currentThread(); while (!sign.compareAndSet(null, current)) { } } // 釋放鎖 public void unlock() { Thread current = Thread.currentThread(); sign.compareAndSet(current, null); }}

o若有同一線程兩調(diào)用lock(),會導(dǎo)致第二次調(diào)用lock位置進行自旋,產(chǎn)生了死鎖說明這個鎖并不是可重入的。(在lock函數(shù)內(nèi),應(yīng)驗證線程是否為已經(jīng)獲得鎖的線程)

o若1問題已經(jīng)解決,當unlock()第一次調(diào)用時,就已經(jīng)將鎖釋放了。實際上不應(yīng)釋放鎖

自旋鎖避免死鎖的方法(采用計數(shù)次統(tǒng)計):

12345678910111213141516171819202122232425262728293031323334353637383940/** * 自旋鎖改進 * * @author Guoqw * @since 2016-01-14 14:11 */public class SpinLockImprove { private AtomicReference owner = new AtomicReference<>(); private int count = 0; /** * 獲取鎖 */ public void lock() { Thread current = Thread.currentThread(); if (current == owner.get()) { count++; return; } while (!owner.compareAndSet(null, current)) { } } /** * 釋放鎖 */ public void unlock() { Thread current = Thread.currentThread(); if (current == owner.get()) { if (count != 0) { count--; } else { owner.compareAndSet(current, null); } } }}

改進后自旋鎖即為重入鎖的簡單實現(xiàn)。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    国产精品欧美一区两区| 亚洲天堂精品一区二区| 婷婷色香五月综合激激情| 加勒比人妻精品一区二区| 69精品一区二区蜜桃视频| 91香蕉视频精品在线看| 日韩一区二区三区久久| 欧美日韩成人在线一区| 亚洲一区二区三区三州| 日韩高清中文字幕亚洲| 人妻少妇系列中文字幕| 邻居人妻人公侵犯人妻视频| 国产在线视频好看不卡| 国产午夜福利在线免费观看| 国产精品不卡一区二区三区四区 | 在线观看视频成人午夜| 国产中文字幕一二三区| 日韩午夜老司机免费视频| 国产成人国产精品国产三级| 一区二区三区四区亚洲专区| 国产精品亚洲精品亚洲| 久久女同精品一区二区| 欧美黑人精品一区二区在线| 久草视频这里只是精品| 丰满少妇被猛烈插入在线观看 | 国产欧美一区二区色综合| 在线日韩中文字幕一区 | 欧美视频在线观看一区| 欧美日韩人妻中文一区二区| 人妻熟女中文字幕在线| 国产乱人伦精品一区二区三区四区| 亚洲av在线视频一区| 国产精品欧美一级免费| 色婷婷日本视频在线观看| 欧美黑人黄色一区二区| 男人和女人黄 色大片| 少妇熟女精品一区二区三区| 日韩精品中文字幕在线视频| 精品老司机视频在线观看| 国产综合一区二区三区av| 国产亚洲午夜高清国产拍精品|