聊聊高并發(fā)(六)實(shí)現(xiàn)幾種自旋鎖(一)

聊聊高并發(fā)(五)理解緩存一致性協(xié)議以及對并發(fā)編程的影響 我們了解了處理器緩存一致性協(xié)議的原理,并且提到了它對并發(fā)編程的影響残炮,“多個線程對同一個變量一直使用CAS操作,那么會有大量修改操作近她,從而產(chǎn)生大量的緩存一致性流量请唱,因?yàn)槊恳淮蜟AS操作都會發(fā)出廣播通知其他處理器跋选,從而影響程序的性能棍好〗锛牛”

這一篇我們通過兩種實(shí)現(xiàn)自旋鎖的方式來看一下不同的編程方式帶來的程序性能的變化识颊。

先理解一下什么是自旋崭庸,所謂自旋就是線程在不滿足某種條件的情況下,一直循環(huán)做某個動作谊囚。所以對于自旋鎖來鎖怕享,當(dāng)線程在沒有獲取鎖的情況下,一直循環(huán)嘗試獲取鎖镰踏,直到真正獲取鎖函筋。

聊聊高并發(fā)(三)鎖的一些基本概念 我們提到鎖的本質(zhì)就是等待,那么如何等待呢奠伪,有兩種方式

1. 線程阻塞

2. 線程自旋

阻塞的缺點(diǎn)顯而易見跌帐,線程一旦進(jìn)入阻塞(Block),再被喚醒的代價比較高绊率,性能較差谨敛。自旋的優(yōu)點(diǎn)是線程還是Runnable的,只是在執(zhí)行空代碼滤否。當(dāng)然一直自旋也會白白消耗計算資源脸狸,所以常見的做法是先自旋一段時間,還沒拿到鎖就進(jìn)入阻塞。JVM在處理synchrized實(shí)現(xiàn)時就是采用了這種折中的方案炊甲,并提供了調(diào)節(jié)自旋的參數(shù)泥彤。

這篇說一下兩種最基本的自旋鎖實(shí)現(xiàn),并提供了一種優(yōu)化的鎖卿啡,后續(xù)會有更多的自旋鎖的實(shí)現(xiàn)吟吝。

首先是TASLock (Test And Set Lock),測試-設(shè)置鎖颈娜,它的特點(diǎn)是自旋時剑逃,每次嘗試獲取鎖時,采用了CAS操作官辽,不斷的設(shè)置鎖標(biāo)志位炕贵,當(dāng)鎖標(biāo)志位可用時,一個線程拿到鎖野崇,其他線程繼續(xù)自旋称开。

缺點(diǎn)是CAS操作一直在修改共享變量的值,會引發(fā)緩存一致性流量風(fēng)暴

package com.test.lock;
 
// 鎖接口
public interface Lock {
    public void lock();
    
    public void unlock();
}
 
 
 
 
package com.test.lock;
 
import java.util.concurrent.atomic.AtomicBoolean;
 
/**
 * 測試-設(shè)置自旋鎖乓梨,使用AtomicBoolean原子變量保存狀態(tài)
 * 每次都使用getAndSet原子操作來判斷鎖狀態(tài)并嘗試獲取鎖
 * 缺點(diǎn)是getAndSet底層使用CAS來實(shí)現(xiàn)鳖轰,一直在修改共享變量的值,會引發(fā)緩存一致性流量風(fēng)暴
 * **/
public class TASLock implements Lock{
    private AtomicBoolean mutex = new AtomicBoolean(false);
    
    @Override
    public void lock() {
        // getAndSet方法會設(shè)置mutex變量為true扶镀,并返回mutex之前的值
        // 當(dāng)mutex之前是false時才返回蕴侣,表示獲取鎖
        // getAndSet方法是原子操作,mutex原子變量的改動對所有線程可見
        while(mutex.getAndSet(true)){
            
        }
    }
 
    @Override
    public void unlock() {
        mutex.set(false);
    }
 
    public String toString(){
        return "TASLock";
    }
}

一種改進(jìn)的算法是TTASLock(Test Test And Set Lock)測試-測試-設(shè)置鎖臭觉,特點(diǎn)是在自旋嘗試獲取鎖時昆雀,分為兩步,第一步通過讀操作來獲取鎖狀態(tài)蝠筑,當(dāng)鎖可獲取時狞膘,第二步再通過CAS操作來嘗試獲取鎖,減少了CAS的操作次數(shù)什乙。并且第一步的讀操作是處理器直接讀取自身高速緩存挽封,不會產(chǎn)生緩存一致性流量,不占用總線資源臣镣。

缺點(diǎn)是在鎖高爭用的情況下辅愿,線程很難一次就獲取鎖,CAS的操作會大大增加忆某。

    package com.test.lock;
     
    import java.util.concurrent.atomic.AtomicBoolean;
     
    /**
     * 測試-測試-設(shè)置自旋鎖点待,使用AtomicBoolean原子變量保存狀態(tài)
     * 分為兩步來獲取鎖
     * 1. 先采用讀變量自旋的方式嘗試獲取鎖
     * 2. 當(dāng)有可能獲取鎖時,再使用getAndSet原子操作來嘗試獲取鎖
     * 優(yōu)點(diǎn)是第一步使用讀變量的方式來獲取鎖弃舒,在處理器內(nèi)部高速緩存操作癞埠,不會產(chǎn)生緩存一致性流量
     * 缺點(diǎn)是當(dāng)鎖爭用激烈的時候,第一步一直獲取不到鎖,getAndSet底層使用CAS來實(shí)現(xiàn)燕差,一直在修改共享變量的值,會引發(fā)緩存一致性流量風(fēng)暴
     * **/
    public class TTASLock implements Lock{
     
    private AtomicBoolean mutex = new AtomicBoolean(false);
        
        @Override
        public void lock() {
            while(true){
                // 第一步使用讀操作坝冕,嘗試獲取鎖徒探,當(dāng)mutex為false時退出循環(huán),表示可以獲取鎖
                while(mutex.get()){}
                // 第二部使用getAndSet方法來嘗試獲取鎖
                if(!mutex.getAndSet(true)){
                    return;
                }   
                
            }
        }
     
        @Override
        public void unlock() {
            mutex.set(false);
        }
     
        public String toString(){
            return "TTASLock";
        }
    }

針對鎖高爭用的問題喂窟,可以采取回退算法测暗,即當(dāng)線程沒有拿到鎖時,就等待一段時間再去嘗試獲取鎖磨澡,這樣可以減少鎖的爭用碗啄,提高程序的性能。

package com.test.lock;
 
import java.util.Random;
 
/**
 * 回退算法稳摄,降低鎖爭用的幾率
 * **/
public class Backoff {
    private final int minDelay, maxDelay;
    
    private int limit;
    
    final Random random;
    
    public Backoff(int min, int max){
        this.minDelay = min;
        this.maxDelay = max;
        limit = minDelay;
        random = new Random();
    }
    
    // 回退稚字,線程等待一段時間
    public void backoff() throws InterruptedException{
        int delay = random.nextInt(limit);
        limit = Math.min(maxDelay, 2 * limit);
        Thread.sleep(delay);
    }
}
 
package com.test.lock;
 
import java.util.concurrent.atomic.AtomicBoolean;
 
/**
 * 回退自旋鎖,在測試-測試-設(shè)置自旋鎖的基礎(chǔ)上增加了線程回退厦酬,降低鎖的爭用
 * 優(yōu)點(diǎn)是在鎖高爭用的情況下減少了鎖的爭用胆描,提高了執(zhí)行的性能
 * 缺點(diǎn)是回退的時間難以控制,需要不斷測試才能找到合適的值仗阅,而且依賴底層硬件的性能昌讲,擴(kuò)展性差
 * **/
public class BackoffLock implements Lock{
 
    private final int MIN_DELAY, MAX_DELAY;
    
    public BackoffLock(int min, int max){
        MIN_DELAY = min;
        MAX_DELAY = max;
    }
    
    private AtomicBoolean mutex = new AtomicBoolean(false);
    
    @Override
    public void lock() {
        // 增加回退對象
        Backoff backoff = new Backoff(MIN_DELAY, MAX_DELAY);
        while(true){
            // 第一步使用讀操作,嘗試獲取鎖减噪,當(dāng)mutex為false時退出循環(huán)短绸,表示可以獲取鎖
            while(mutex.get()){}
            // 第二部使用getAndSet方法來嘗試獲取鎖
            if(!mutex.getAndSet(true)){
                return;
            }else{
                //回退
                try {
                    backoff.backoff();
                } catch (InterruptedException e) {
                }
            }    
            
        }
    }
 
    @Override
    public void unlock() {
        mutex.set(false);
    }
 
    public String toString(){
        return "TTASLock";
    }
}

回退自旋鎖的問題是回退的時間難以控制,需要不斷測試才能找到合適的值筹裕,而且依賴底層硬件的性能醋闭,擴(kuò)展性差。后面會有更好的自旋鎖實(shí)現(xiàn)算法朝卒。

下面我們測試一下TASLock和TTASLock的性能目尖。

首先寫一個計時的類

package com.test.lock;
 
public class TimeCost implements Lock{
 
    private final Lock lock;
    
    public TimeCost(Lock lock){
        this.lock = lock;
    }
    
    @Override
    public void lock() {
        long start = System.nanoTime();
        lock.lock();
        long duration = System.nanoTime() - start;
        System.out.println(lock.toString() + " time cost is " + duration + " ns");
    }
 
    @Override
    public void unlock() {
        lock.unlock();
    }
 
}

然后采用多個線程來模擬對同一把鎖的爭用

package com.test.lock;
 
public class Main {
    private static TimeCost timeCost = new TimeCost(new TASLock());
    
    //private static TimeCost timeCost = new TimeCost(new TTASLock());
    
    public static void method(){
        timeCost.lock();
        //int a = 10;
        timeCost.unlock();
    }
    
    public static void main(String[] args) {
        for(int i = 0; i < 100; i ++){
            Thread t = new Thread(new Runnable(){
    
                @Override
                public void run() {
                    method();
                }
                
            });
            t.start();
        }
    }
 
}

測試機(jī)器的性能如下:

CPU: 4 Intel(R) Core(TM) i3-2120 CPU @ 3.30GHz

內(nèi)存: 8G

測試結(jié)果:

50個線程情況下:

TASLock平均獲取鎖的時間: 339715 ns

TTASLock平均獲取鎖的時間: 67106.2 ns

100個線程情況下:

TASLock平均獲取鎖的時間: 1198413 ns

TTASLock平均獲取鎖的時間: 1273588 ns

可以看到TTASLock的性能比TASLock的性能更好

轉(zhuǎn)載請注明來源: http://blog.csdn.net/iter_zc

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市扎运,隨后出現(xiàn)的幾起案子瑟曲,更是在濱河造成了極大的恐慌,老刑警劉巖豪治,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洞拨,死亡現(xiàn)場離奇詭異,居然都是意外死亡负拟,警方通過查閱死者的電腦和手機(jī)烦衣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人花吟,你說我怎么就攤上這事秸歧。” “怎么了衅澈?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵键菱,是天一觀的道長。 經(jīng)常有香客問我今布,道長经备,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任部默,我火速辦了婚禮侵蒙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘傅蹂。我一直安慰自己纷闺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布份蝴。 她就那樣靜靜地躺著急但,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搞乏。 梳的紋絲不亂的頭發(fā)上波桩,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機(jī)與錄音请敦,去河邊找鬼镐躲。 笑死,一個胖子當(dāng)著我的面吹牛侍筛,可吹牛的內(nèi)容都是我干的萤皂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼匣椰,長吁一口氣:“原來是場噩夢啊……” “哼裆熙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起禽笑,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤入录,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后佳镜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體僚稿,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年蟀伸,在試婚紗的時候發(fā)現(xiàn)自己被綠了蚀同。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缅刽。...
    茶點(diǎn)故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蠢络,靈堂內(nèi)的尸體忽然破棺而出衰猛,到底是詐尸還是另有隱情,我是刑警寧澤刹孔,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布啡省,位于F島的核電站,受9級特大地震影響芦疏,放射性物質(zhì)發(fā)生泄漏冕杠。R本人自食惡果不足惜微姊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一酸茴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧兢交,春花似錦薪捍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至晴裹,卻和暖如春被济,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背涧团。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工只磷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人泌绣。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓钮追,卻偏偏與公主長得像,于是被迫代替她去往敵國和親阿迈。 傳聞我的和親對象是個殘疾皇子元媚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評論 2 355

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