31.Android架構-線程(一)

死鎖

是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖。
死鎖發生的條件
1)互斥條件:指進程對所分配到的資源進行排它性使用,即在一段時間內某資源只由一個進程佔用。如果此時還有其它進程請求資源,則請求者只能等待,直至佔有資源的進程用畢釋放。
2)請求和保持條件:指進程已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程佔有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。
3)不剝奪條件:指進程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。
4)循環等待條件:指在發生死鎖時,必然存在一個進程——資源的環形鏈,即進程集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1佔用的資源;P1正在等待P2佔用的資源,……,Pn正在等待已被P0佔用的資源。

避免死鎖常見的算法有有序資源分配法、銀行家算法。

活鎖

兩個線程在嘗試拿鎖的機制中,發生多個線程之間互相謙讓,不斷髮生同一個線程總是拿到同一把鎖,在嘗試拿另一把鎖時因爲拿不到,而將本來已經持有的鎖釋放的過程。
解決辦法:每個線程休眠隨機數,錯開拿鎖的時間。

線程飢餓

低優先級的線程,總是拿不到執行時間

ThreadLocal


爲什麼ThreadLocal能保證每個線程的變量互不干擾呢?
如圖,其實在每個線程中都有一個成員變量threadLocalMap

/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocalMap這個類內部又維護了一個Entry數組,而Entry則是一個可以保存key value的對象,當我們創建一個ThreadLocal保存一個線程中某個特有變量的值時,他會先拿到當前線程的對象,然後從線程中取出當前線程的成員便量ThreadLocalMap,然後將這個值以這個ThreadLocal變量爲key,以他本身爲value放入ThreadLocalMap中的Entry數組中,從而實現了變量的保存

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

那麼他是如何保證每個線程中的值互不影響的?
當我們需要取出這個線程中值的時候,同樣仍然是獲取到當前線程中的ThreadLocalMap,然後獲取到ThreadLocalMap中以這個ThreadLocal對象爲key的value值。所以我們看到這裏可以明白了,當我們從某個線程中取出某個值的時候,我們其實是從當前線程維護的ThreadLocalMap中取的,所以每個線程之間當然互不干擾了

CAS

首先,原子操作

假定有兩個操作A和B(A和B可能都很複雜),如果從執行A的線程來看,當另一個線程執行B時,要麼將B全部執行完,要麼完全不執行B,那麼A和B對彼此來說是原子的。

然後,CAS可以實現原子操作

每一個CAS操作過程都包含三個運算符:一個內存地址V,一個期望的值A和一個新值B,操作的時候如果這個地址上存放的值等於這個期望的值A,則將地址上的值賦爲新值B,否則不做任何操作。
CAS的基本思路就是,如果這個地址上的值和期望的值相等,則給其賦予新值,否則不做任何事兒,但是要返回原值是多少。循環CAS就是在一個循環裏不斷的做cas操作,直到成功爲止。

最後,CAS存在的問題

1.ABA問題

因爲CAS需要在操作值的時候,檢查值有沒有發生變化,如果沒有發生變化則更新,但是如果一個值原來是A,變成了B,又變成了A,那麼使用CAS進行檢查時會發現它的值沒有發生變化,但是實際上卻變化了。
ABA問題的解決思路就是使用版本號。在變量前面追加上版本號,每次變量更新的時候把版本號加1,那麼A→B→A就會變成1A→2B→3A

2.循環時間長開銷大

自旋CAS如果長時間不成功,會給CPU帶來非常大的執行開銷。

3.只能保證一個共享變量的原子操作

當對一個共享變量執行操作時,我們可以使用循環CAS的方式來保證原子操作,但是對多個共享變量操作時,循環CAS就無法保證操作的原子性,這個時候就可以用鎖。
還有一個取巧的辦法,就是把多個共享變量合併成一個共享變量來操作。比如,有兩個共享變量i=2,j=a,合併一下ij=2a,然後用CAS來操作ij。從Java 1.5開始,JDK提供了AtomicReference類來保證引用對象之間的原子性,就可以把多個變量放在一個對象裏來進行CAS操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章