公平鎖/非公平鎖/可重入鎖/遞歸鎖/自旋鎖談?wù)勀愕睦斫猓空?qǐng)手寫一個(gè)自旋鎖

公平鎖和非公平鎖是什么弥奸?有什么區(qū)別?

  1. 并發(fā)包中ReentrantLock的創(chuàng)建可以指定構(gòu)造函數(shù)的布爾類型來(lái)得到公平鎖和非公平鎖奋早,默認(rèn)是非公平鎖盛霎。
    ReentrantLock lock = new ReentrantLock(true);
  1. 兩者的區(qū)別
    • 公平鎖:就是很公平赠橙,在并發(fā)環(huán)境中,每個(gè)線程在獲取鎖時(shí)會(huì)查看此鎖維護(hù)的等待隊(duì)列愤炸,如果為空期揪,或者當(dāng)前線程時(shí)等待隊(duì)列的第一個(gè),就占有鎖摇幻。否則就會(huì)加入到等待隊(duì)列中,以后會(huì)按照FIFO規(guī)則在隊(duì)列中取到自己挥萌。
    • 非公平鎖:上來(lái)就直接占有鎖绰姻,如果嘗試失敗,就再采用類似公平鎖的那種方式引瀑。非公平鎖的優(yōu)點(diǎn)在于吞吐量比公平鎖大狂芋。

Synchronized也是一種非公平鎖。

可重入鎖(也叫遞歸鎖)

指同一線程在外層方法獲取鎖的時(shí)候憨栽,在內(nèi)層方法會(huì)自動(dòng)獲取鎖帜矾。
ReentrantLock和Synchronized是典型的可重入鎖
可重入鎖最大的作用是避免死鎖。

自旋鎖(spinLock)

是指嘗試獲取鎖的線程不會(huì)阻塞屑柔,而是采用循環(huán)的方式獲取鎖

  • 好處:減少上下文切換的消耗
  • 壞處:循環(huán)會(huì)消耗CPU
public final int getAndSetInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var4));

        return var5;
    }
  • 手寫自旋鎖
public class SpinLockDemo {


    //原子引用線程
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void myLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\t comeIn");
        while (!atomicReference.compareAndSet(null, thread)) {

        }
    }

    public void myUnLock() {
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread, null);
        System.out.println(Thread.currentThread().getName() + "\t myUnlock ");
    }

    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();

        new Thread(() -> {
            spinLockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockDemo.myUnLock();

        }, "t1").start();

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            spinLockDemo.myLock();
            spinLockDemo.myUnLock();
        }, "t2").start();

    }
}

獨(dú)占鎖(寫鎖)屡萤,共享鎖(讀鎖),互斥鎖

  1. 獨(dú)占鎖是指鎖一次只能被一個(gè)線程所持有掸宛,對(duì)ReentrantLock和Synchronized而言死陆,都是獨(dú)占鎖。
  2. 共享鎖是指該鎖可能被多個(gè)線程所持有唧瘾。

對(duì)ReentrantWriteReadLock其讀鎖是共享鎖措译,其寫鎖是獨(dú)占鎖,寫寫饰序,讀寫领虹,寫讀過(guò)程都是互斥的。也就是多線程下讀的時(shí)候不寫求豫,寫的時(shí)候不讀塌衰,讀的時(shí)候其他線程可以讀。

  • 讀寫鎖案例蝠嘉,手寫一個(gè)緩存
class myCache {//資源類
    private volatile Map<String, Object> map = new HashMap<>();
    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void put(String key, Object value) {
        lock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t 正在寫入:key:" + key);
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "\t 寫入完成");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.writeLock().unlock();
        }
    }

    public void get(String key) {
        lock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t 開始讀猾蒂,key:" + key);
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Object result = map.get(key);
            System.out.println(Thread.currentThread().getName() + "\t 讀完成,result:" + result);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
          lock.readLock().unlock();
        }
    }
}

/**
 * @author liujian
 * @descripts 讀寫鎖
 * @create 2019-06-22 17:26
 * <p>
 * 寫操作:獨(dú)占+原子,中間的過(guò)程必須是完整的統(tǒng)一體是晨,不允許被打斷肚菠,被分割
 */
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        myCache myCache = new myCache();
        for (int i = 0; i < 50; i++) {
            final int tempInt = i;
            new Thread(() -> {
                myCache.put(String.valueOf(tempInt), String.valueOf(tempInt));
            }, String.valueOf(i)).start();
        }
        for (int i = 0; i < 50; i++) {
            final int tempInt = i;
            new Thread(() -> {
                myCache.get(String.valueOf(tempInt));
            }, String.valueOf(i)).start();
        }
    }
}

Synchronized和Lock有什么區(qū)別?用新的Lock有什么好處罩缴?舉例說(shuō)明

1. 原始構(gòu)成

Synchronized是關(guān)鍵字蚊逢,屬于JVM層面层扶。

  • monitorenter 底層通過(guò)monitor對(duì)象完成,其實(shí)wait/notify等方法也依賴monitor對(duì)象烙荷,只有在代碼塊和方法中才可以使用wait/notify等方法镜会。
  • monitorexit

Lock是具體的類(java.concurrent.locks.lock),是API層面的鎖终抽。

2. 使用方法
  • Synchronized不需要用戶手動(dòng)去釋放鎖戳表,當(dāng)Synchronized代碼執(zhí)行完后,系統(tǒng)會(huì)自動(dòng)讓線程釋放對(duì)鎖的占用昼伴。
  • ReentrantLock 需要用戶手動(dòng)去釋放鎖匾旭,如果不釋放,會(huì)造成死鎖的現(xiàn)象圃郊。
3. 等待是否可中斷

synchronized不可中斷价涝,只有拋出異常和運(yùn)行完成兩種情況。
ReentrantLock 可中斷

  • 設(shè)置超時(shí)方法中斷:tryLock(Long timeout ,TimeUnit unit)
  • 在代碼塊中聲明方法:lock.lockInterruptibly(); 當(dāng)線程調(diào)用Thread.interrupted()時(shí)持舆,等待鎖的過(guò)程中會(huì)立即響應(yīng)中斷色瘩。
4. 加鎖是否公平

Synchronized 是非公平鎖
ReentrantLock 默認(rèn)非公平鎖,構(gòu)建函數(shù)傳入boolean值逸寓,true為公平鎖居兆,false為非公平鎖。

public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
5. 鎖綁定多個(gè)條件

Synchronized沒(méi)有
ReentrantLock用來(lái)分組喚醒需要喚醒的線程們竹伸,可以精確喚醒史辙,而不是像synchronized要么喚醒一個(gè)線程要么喚醒全部線程。

demo演示

題目:多線程之間按順序調(diào)用佩伤,實(shí)現(xiàn)A->B->C三個(gè)線程啟動(dòng)聊倔,要求如下:
AA打印五次,BB打印十次生巡,CC打印15次
緊接著 AA打印五次耙蔑,BB打印十次,CC打印15次
...
來(lái)10輪

public class SynAndReentrantLockDemo {
    public static void main(String[] args) {
        ShareResource shareResource = new ShareResource();
        for (int i = 0; i <10 ; i++) {
            new Thread(() -> shareResource.print(5), "AA").start();
            new Thread(() -> shareResource.print(10), "BB").start();
            new Thread(() -> shareResource.print(15), "CC").start();
        }
    }
}

class ShareResource {
    private int num = 1;
    private Lock lock = new ReentrantLock();
    private Condition c1 = lock.newCondition();
    private Condition c2 = lock.newCondition();
    private Condition c3 = lock.newCondition();

    public void print(int j) {
        lock.lock();
        try {
            if (j==5){
                while (num != 1) {
                    c1.await();
                }
                for (int i = 0; i < j; i++) {
                    System.out.println(Thread.currentThread().getName() + "\t ");
                }
                num = 2;
                c2.signal();
            }else if (j==10){
                while (num != 2) {
                    c2.await();
                }
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + "\t ");
                }
                c3.signal();
                num = 3;
            }else if (j==15){
                while (num != 3) {
                    c3.await();
                }
                for (int i = 0; i < 15; i++) {
                    System.out.println(Thread.currentThread().getName() + "\t ");
                }
                c1.signal();
                num = 1;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末孤荣,一起剝皮案震驚了整個(gè)濱河市甸陌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌盐股,老刑警劉巖钱豁,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異疯汁,居然都是意外死亡牲尺,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)谤碳,“玉大人溃卡,你說(shuō)我怎么就攤上這事⊙鸭颍” “怎么了瘸羡?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)搓茬。 經(jīng)常有香客問(wèn)我犹赖,道長(zhǎng),這世上最難降的妖魔是什么卷仑? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任峻村,我火速辦了婚禮,結(jié)果婚禮上系枪,老公的妹妹穿的比我還像新娘雀哨。我一直安慰自己磕谅,他們只是感情好私爷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著膊夹,像睡著了一般衬浑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上放刨,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天工秩,我揣著相機(jī)與錄音,去河邊找鬼进统。 笑死助币,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的螟碎。 我是一名探鬼主播眉菱,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼掉分!你這毒婦竟也來(lái)了俭缓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤酥郭,失蹤者是張志新(化名)和其女友劉穎华坦,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體不从,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡惜姐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了椿息。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片载弄。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡耘拇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宇攻,到底是詐尸還是另有隱情惫叛,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布逞刷,位于F島的核電站嘉涌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏夸浅。R本人自食惡果不足惜仑最,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望帆喇。 院中可真熱鬧警医,春花似錦、人聲如沸坯钦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)婉刀。三九已至吟温,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間突颊,已是汗流浹背鲁豪。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留律秃,地道東北人爬橡。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像棒动,于是被迫代替她去往敵國(guó)和親橄唬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子隙券,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容