線程同步和并發(fā)

  • CPU-高速緩存-主存
    在主流計(jì)算機(jī)的設(shè)計(jì)中,CPU的運(yùn)算速度比主內(nèi)存的讀寫(xiě)速度要快得多,這就使得CPU在訪問(wèn)內(nèi)存時(shí)要花很長(zhǎng)時(shí)間來(lái)等待內(nèi)存的操作,這種空等造成了系統(tǒng)整體性能的下降。為了解決這種速度上的不匹配問(wèn)題,我們?cè)贑PU與主內(nèi)存之間加入了比主內(nèi)存要快的SRAM(Static Ram,靜態(tài)存儲(chǔ)器)。SRAM儲(chǔ)存了主內(nèi)存的映象,使CPU可以直接通過(guò)訪問(wèn)SRAM來(lái)完成數(shù)據(jù)的讀寫(xiě)。由于SRAM的速度與CPU的速度相當(dāng),從而大大縮短了數(shù)據(jù)讀寫(xiě)的等待時(shí)間,系統(tǒng)的整體速度也自然得到提高。 高速緩存即 Cache,就是指介于CPU與主內(nèi)存之間的高速存儲(chǔ)器(通常由靜態(tài)存儲(chǔ)器SRAM構(gòu)成)。
    Cache的工作原理是基于程序訪問(wèn)的局部性。依據(jù)局部性原理,可以在主存和CPU通用寄存器之間設(shè)置一個(gè)高速的容量相對(duì)較小的存儲(chǔ)器,把正在執(zhí)行的指令地址附近的一部分指令或數(shù)據(jù)從主存調(diào)入這個(gè)存儲(chǔ)器,供CPU在一段時(shí)間內(nèi)使用。這對(duì)提高程序的運(yùn)行速度有很大的作用。這個(gè)介于主存和CPU之間的高速小容量存儲(chǔ)器稱(chēng)作高速緩沖存儲(chǔ)器(Cache)。
    順序流程.jpg

每個(gè)線程都有自己的高速緩存,在多線并發(fā)過(guò)程中,會(huì)導(dǎo)致CPU訪問(wèn)到高速緩存的數(shù)據(jù)不一致,從而導(dǎo)致線程并發(fā)問(wèn)題。

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

    • 原子性

      執(zhí)行不可以分割,代碼1、2、3要么都執(zhí)行,要么都不執(zhí)行。例如A 匯錢(qián)給 B,需要保證在數(shù)據(jù)修改時(shí),A和B的賬戶(hù)同時(shí)修改(成功),或者同時(shí)不修改(不成功)。

    • 可見(jiàn)性

      當(dāng)多線程訪問(wèn)同一個(gè)共享變量時(shí),一個(gè)線程修改變量的值,保證了其他線程及時(shí)可以看到變量的修改,例如:volatile 關(guān)鍵字,保證變量修改的可見(jiàn)性。

    • 有序性

      程序執(zhí)行的順序按照代碼的先后順序執(zhí)行:在很多編譯器中,會(huì)對(duì)處理指令重新排序,提高執(zhí)行效率,但是只會(huì)排序于數(shù)據(jù)無(wú)關(guān)的語(yǔ)句,保持線程內(nèi)結(jié)果一致性。例如:java內(nèi)存模型中,有happens-before原則。在java中,重新指令排序不僅僅涉及到結(jié)果的一致性,還涉及到GC執(zhí)行調(diào)用--重新排序的指令段執(zhí)行時(shí),觸發(fā)GC會(huì)導(dǎo)致垃圾回收的不可靠,因此GC的觸發(fā)并不是在所有的代碼執(zhí)行時(shí)都可以觸發(fā),而是存在一定的安全區(qū)域,在安全區(qū)域內(nèi)才能觸發(fā)GC,有興趣的同事可以去深入了解一下。

  • 線程同步

    • Synchronized修飾

    synchronized可以保證變量的修改可見(jiàn)性和原子性,當(dāng)前線程可以訪問(wèn)該變量,其他線程被阻塞住

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

volatile本質(zhì)是:jvm當(dāng)前變量在寄存器中的值是不確定的,需要從主存中讀取

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

lock鎖的狀態(tài)是可見(jiàn)的,lock鎖的類(lèi)型比較多:可重入鎖,讀寫(xiě)鎖,使用方法更加靈活。lock必須在finally釋放,否則可能會(huì)造成異常情況下,鎖無(wú)法釋放

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

ConcurrentHashMap BlockingQueue等數(shù)據(jù)結(jié)構(gòu)
在java中,由于hashmap等容器是非線程安全的,java提供對(duì)用concurrentXXX數(shù)據(jù)結(jié)構(gòu),多線程安全,而且效率非常高,有興趣的同事可以去看下

  • ThreadLocal

ThreadLocal比較常用,其本質(zhì)不是線程同步,而是以空間換時(shí)間,每個(gè)線程維護(hù)自己的副本(在后臺(tái)并發(fā)時(shí)比較有用)

    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();
    }

測(cè)試:

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();
}

結(jié)果:

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

其原因是ThreadLocal在ThreadLocalMap中為每個(gè)線程保存了副本,map的key就是Thread本身,ThreadLocalMap持有ThreadLocal的弱引用,保證線程釋放資源時(shí)的內(nèi)存泄露問(wèn)題(ThreadLoacl變量建議設(shè)置成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);
     }
  • 總結(jié)
    以上簡(jiǎn)單描述了CPU-Cache-主存模型,并發(fā)和線程同步,具體的實(shí)現(xiàn)大家可以多看看源碼和第三方開(kāi)源框架,里面很多設(shè)計(jì)非常值得我們學(xué)習(xí)和借鑒。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文是我自己在秋招復(fù)習(xí)時(shí)的讀書(shū)筆記,整理的知識(shí)點(diǎn),也是為了防止忘記,尊重勞動(dòng)成果,轉(zhuǎn)載注明出處哦!如果你也喜歡,那...
    波波波先森閱讀 11,589評(píng)論 4 56
  • CPU Cache 今天的CPU比25年前更復(fù)雜。那時(shí)候,CPU內(nèi)核的頻率與內(nèi)存總線的頻率相當(dāng)。內(nèi)存訪問(wèn)只比寄存器...
    blueshadow閱讀 3,216評(píng)論 0 5
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂(lè)視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,755評(píng)論 11 349
  • 戲子有點(diǎn)兒痞閱讀 223評(píng)論 0 0
  • 暮色四合,蟲(chóng)鳴漸起。未知的黑暗深處,我跋山涉水而來(lái)。尋找光明或者說(shuō)撲火是我的宿命,不管怎樣我都會(huì)擁抱著火...
    鰹魚(yú)干閱讀 242評(píng)論 0 0

友情鏈接更多精彩內(nèi)容