線程同步和并發(fā)

  • CPU-高速緩存-主存
    在主流計算機的設計中介陶,CPU的運算速度比主內存的讀寫速度要快得多学搜,這就使得CPU在訪問內存時要花很長時間來等待內存的操作娃豹,這種空等造成了系統(tǒng)整體性能的下降憔涉。為了解決這種速度上的不匹配問題讨勤,我們在CPU與主內存之間加入了比主內存要快的SRAM(Static Ram箭跳,靜態(tài)存儲器)。SRAM儲存了主內存的映象潭千,使CPU可以直接通過訪問SRAM來完成數(shù)據(jù)的讀寫谱姓。由于SRAM的速度與CPU的速度相當,從而大大縮短了數(shù)據(jù)讀寫的等待時間刨晴,系統(tǒng)的整體速度也自然得到提高屉来。 高速緩存即 Cache,就是指介于CPU與主內存之間的高速存儲器(通常由靜態(tài)存儲器SRAM構成)狈癞。
    Cache的工作原理是基于程序訪問的局部性茄靠。依據(jù)局部性原理,可以在主存和CPU通用寄存器之間設置一個高速的容量相對較小的存儲器蝶桶,把正在執(zhí)行的指令地址附近的一部分指令或數(shù)據(jù)從主存調入這個存儲器慨绳,供CPU在一段時間內使用。這對提高程序的運行速度有很大的作用真竖。這個介于主存和CPU之間的高速小容量存儲器稱作高速緩沖存儲器(Cache)脐雪。
    順序流程.jpg

每個線程都有自己的高速緩存,在多線并發(fā)過程中恢共,會導致CPU訪問到高速緩存的數(shù)據(jù)不一致战秋,從而導致線程并發(fā)問題。

  • 并發(fā)的三個概念

    • 原子性

      執(zhí)行不可以分割讨韭,代碼1脂信、2癣蟋、3要么都執(zhí)行,要么都不執(zhí)行狰闪。例如A 匯錢給 B梢薪,需要保證在數(shù)據(jù)修改時,A和B的賬戶同時修改(成功)尝哆,或者同時不修改(不成功)秉撇。

    • 可見性

      當多線程訪問同一個共享變量時,一個線程修改變量的值秋泄,保證了其他線程及時可以看到變量的修改琐馆,例如:volatile 關鍵字,保證變量修改的可見性恒序。

    • 有序性

      程序執(zhí)行的順序按照代碼的先后順序執(zhí)行:在很多編譯器中瘦麸,會對處理指令重新排序,提高執(zhí)行效率歧胁,但是只會排序于數(shù)據(jù)無關的語句滋饲,保持線程內結果一致性。例如:java內存模型中喊巍,有happens-before原則屠缭。在java中,重新指令排序不僅僅涉及到結果的一致性崭参,還涉及到GC執(zhí)行調用--重新排序的指令段執(zhí)行時呵曹,觸發(fā)GC會導致垃圾回收的不可靠,因此GC的觸發(fā)并不是在所有的代碼執(zhí)行時都可以觸發(fā)何暮,而是存在一定的安全區(qū)域奄喂,在安全區(qū)域內才能觸發(fā)GC,有興趣的同事可以去深入了解一下海洼。

  • 線程同步

    • Synchronized修飾

    synchronized可以保證變量的修改可見性和原子性跨新,當前線程可以訪問該變量,其他線程被阻塞住

    public synchronized void addSession(){
        mSessionCount++;
    }
    
    public void removeSession(){
        synchronized (this) {
            mSessionCount--;
        }
    }
  • volatile (保證可見性,不保證原子性)

volatile本質是:jvm當前變量在寄存器中的值是不確定的,需要從主存中讀取

private volatile int mSessionCount;
  • 原子類(AtomicInterger, AtomicBoolean ......)
  • lock鎖

lock鎖的狀態(tài)是可見的坏逢,lock鎖的類型比較多:可重入鎖域帐,讀寫鎖,使用方法更加靈活词疼。lock必須在finally釋放俯树,否則可能會造成異常情況下,鎖無法釋放

    ReentrantLock mLock = new ReentrantLock();
    public void addSession(){
        mLock.lock();
        try{
            mSessionCount++;
        }finally{
            mLock.unlock();
        }
        
    }
  • 容器類

ConcurrentHashMap BlockingQueue等數(shù)據(jù)結構
在java中贰盗,由于hashmap等容器是非線程安全的,java提供對用concurrentXXX數(shù)據(jù)結構阳欲,多線程安全舵盈,而且效率非常高陋率,有興趣的同事可以去看下

  • ThreadLocal

ThreadLocal比較常用,其本質不是線程同步秽晚,而是以空間換時間瓦糟,每個線程維護自己的副本(在后臺并發(fā)時比較有用)

    private static ThreadLocal<Integer> sCount = new ThreadLocal<Integer>();
    public void addSession(int count){
        count = ((sCount.get() == null) ? 0 : sCount.get()) + count;  
        sCount.set(count);
    }
    
    public int getSession(){
        return sCount.get();
    }

測試:

for(int i = 0; i < 5; i++){
    new Thread(new Runnable() {
        public void run() {
            s.addSession(1);
            System.out.println("session count : " + s.getSession());
        }
    }).start();
}

結果:

session count : 1
session count : 1
session count : 1
session count : 1
session count : 1

其原因是ThreadLocal在ThreadLocalMap中為每個線程保存了副本,map的key就是Thread本身赴蝇,ThreadLocalMap持有ThreadLocal的弱引用菩浙,保證線程釋放資源時的內存泄露問題(ThreadLoacl變量建議設置成private static),ThreadLocal:

static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    /**
     * Removes the current thread's value for this thread-local
     * variable.  If this thread-local variable is subsequently
     * {@linkplain #get read} by the current thread, its value will be
     * reinitialized by invoking its {@link #initialValue} method,
     * unless its value is {@linkplain #set set} by the current thread
     * in the interim.  This may result in multiple invocations of the
     * {@code initialValue} method in the current thread.
     *
     * @since 1.5
     */
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
  • 總結
    以上簡單描述了CPU-Cache-主存模型句伶,并發(fā)和線程同步劲蜻,具體的實現(xiàn)大家可以多看看源碼和第三方開源框架,里面很多設計非常值得我們學習和借鑒考余。
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末先嬉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子楚堤,更是在濱河造成了極大的恐慌疫蔓,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件身冬,死亡現(xiàn)場離奇詭異衅胀,居然都是意外死亡,警方通過查閱死者的電腦和手機酥筝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門拗小,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人樱哼,你說我怎么就攤上這事哀九。” “怎么了搅幅?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵阅束,是天一觀的道長。 經(jīng)常有香客問我茄唐,道長息裸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任沪编,我火速辦了婚禮呼盆,結果婚禮上,老公的妹妹穿的比我還像新娘蚁廓。我一直安慰自己访圃,他們只是感情好,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布相嵌。 她就那樣靜靜地躺著腿时,像睡著了一般况脆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上批糟,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天格了,我揣著相機與錄音,去河邊找鬼徽鼎。 笑死盛末,一個胖子當著我的面吹牛,可吹牛的內容都是我干的否淤。 我是一名探鬼主播悄但,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼叹括!你這毒婦竟也來了算墨?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤汁雷,失蹤者是張志新(化名)和其女友劉穎净嘀,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侠讯,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡挖藏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了厢漩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片膜眠。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖溜嗜,靈堂內的尸體忽然破棺而出宵膨,到底是詐尸還是另有隱情,我是刑警寧澤炸宵,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布辟躏,位于F島的核電站,受9級特大地震影響土全,放射性物質發(fā)生泄漏捎琐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一裹匙、第九天 我趴在偏房一處隱蔽的房頂上張望瑞凑。 院中可真熱鬧,春花似錦概页、人聲如沸籽御。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽篱蝇。三九已至贺待,卻和暖如春徽曲,著一層夾襖步出監(jiān)牢的瞬間零截,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工秃臣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留涧衙,地道東北人。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓奥此,卻偏偏與公主長得像弧哎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子稚虎,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內容

  • 本文是我自己在秋招復習時的讀書筆記撤嫩,整理的知識點,也是為了防止忘記蠢终,尊重勞動成果序攘,轉載注明出處哦!如果你也喜歡寻拂,那...
    波波波先森閱讀 11,244評論 4 56
  • CPU Cache 今天的CPU比25年前更復雜程奠。那時候,CPU內核的頻率與內存總線的頻率相當祭钉。內存訪問只比寄存器...
    blueshadow閱讀 2,984評論 0 5
  • 從三月份找實習到現(xiàn)在瞄沙,面了一些公司,掛了不少慌核,但最終還是拿到小米距境、百度、阿里垮卓、京東垫桂、新浪、CVTE扒接、樂視家的研發(fā)崗...
    時芥藍閱讀 42,213評論 11 349
  • 戲子有點兒痞閱讀 212評論 0 0
  • 暮色四合伪货,蟲鳴漸起。未知的黑暗深處钾怔,我跋山涉水而來碱呼。尋找光明或者說撲火是我的宿命,不管怎樣我都會擁抱著火...
    鰹魚干閱讀 174評論 0 0