并發(fā)相關(guān)知識(shí)點(diǎn)整理

并發(fā),即一個(gè)代碼塊同時(shí)被多個(gè)線程執(zhí)行,代碼塊中的變量會(huì)被同時(shí)的線程修改成不同的值,造成數(shù)據(jù)錯(cuò)亂,運(yùn)行結(jié)果錯(cuò)誤現(xiàn)象產(chǎn)生,如何來避免這一問題產(chǎn)生呢?這就產(chǎn)生了鎖機(jī)制,通過對(duì)代碼塊加鎖,來保證同一時(shí)刻只能有一個(gè)線程來操作數(shù)據(jù),這樣就能保證數(shù)據(jù)的一致性。

一、鎖機(jī)制

  • 重入鎖/不可重入鎖
  • 共享鎖/互斥鎖
  • 樂觀鎖/悲觀鎖
  • 自旋鎖

重入鎖/不可重入鎖

在同一個(gè)線程中,可重復(fù)進(jìn)入的鎖,就可重入鎖。如,一個(gè)線程中進(jìn)入了一個(gè)帶鎖的方法,再次進(jìn)入使用同一個(gè)鎖的其他方法時(shí),不需要再獲取鎖,可直接進(jìn)入。

可重入鎖

  • ReentrantLock
  • synchronized

不可重入鎖

  • NoReentrantLock

共享鎖/互斥鎖

當(dāng)一個(gè)線程獲取到鎖后,其他線程也可以獲取此鎖,則此鎖是共享鎖,如讀鎖;如果其他線程不能獲取此鎖,則此鎖是互斥鎖,如寫鎖

  • ReentrantReadWriteLock

樂觀鎖/悲觀鎖

樂觀鎖認(rèn)為并發(fā)操作一定會(huì)修改數(shù)據(jù);悲觀鎖認(rèn)為并發(fā)操作不會(huì)修改數(shù)據(jù),但在寫數(shù)據(jù)時(shí)一般

樂觀鎖

  • AtomickInteger
  • AtomickXXX

悲觀鎖

  • ReentrantLock
  • synchronized

自旋鎖

一般情況下線程在未獲取到鎖的情況下會(huì)被阻塞,而自旋鎖則是內(nèi)部有一個(gè)循環(huán)體在輪詢鎖是否可用。自旋鎖可以減少線程上下文切換帶來的消耗,但會(huì)消耗CPU。

  • AtomicInteger
  • AtomicXXXX

二、死鎖

線程都要獲取鎖才能進(jìn)行運(yùn)行,但所有的線程都獲取不到,導(dǎo)致出現(xiàn)所有線程都不能運(yùn)行的情況,這種情況就叫作死鎖。

死鎖場(chǎng)景

1. 設(shè)計(jì)不當(dāng)

線程內(nèi)有多個(gè)鎖,一個(gè)線程在獲取鎖A后,需要再次獲取鎖B才能執(zhí)行相關(guān)代碼,而此刻另一線程已經(jīng)獲取到鎖B,但它卻在等待獲取鎖A來執(zhí)行代碼,兩個(gè)線程都要獲取對(duì)方已經(jīng)獲取到的鎖,而又都獲取不到,造成死循環(huán),也就是死鎖現(xiàn)象。

2. 程序異常

線程在獲取鎖后執(zhí)行相關(guān)代碼,執(zhí)行完相關(guān)代碼后釋放鎖。但執(zhí)行過程發(fā)生了異常,導(dǎo)致線程意外退出,獲取到的鎖沒有被釋放,導(dǎo)致其他所有線程都無法獲取鎖的情況。
解決方案:添加try/catch,在finally塊里釋放鎖

class AClass{
    ReentrantLock lock=new ReentrantLock();
    
    void aMethod() {
        lock.lock();
        try {
            doSomeThing();
        } catch(Throwable e){
            e.printStacktrace();
        } finally {      
            lock.unlock();
        }
    }
}

三、synchronized使用方式

synchronized可根據(jù)加鎖范圍分為以下兩種方式,但

  • 鎖對(duì)象
  • 鎖類

鎖對(duì)象

即針對(duì)對(duì)象中的方法進(jìn)行加鎖,一個(gè)對(duì)象的加鎖方法被線程進(jìn)入后,其他線程不能再進(jìn)入此對(duì)象加鎖的方法,但可以進(jìn)入不同對(duì)象的同一加鎖方法。

  1. synchronized修飾普通方法
public synchronized void test1(){
    ...
}
  1. synchronized代碼塊,內(nèi)部使用this或成員變量
public void test1(){
    synchronized(this){
        ...
    }
}

鎖類

類鎖與對(duì)象鎖是一致的,但類方法在內(nèi)存中只有一份。加鎖的是整個(gè)類方法,當(dāng)一個(gè)線程獲取到類方法鎖后,其他線程不能再進(jìn)入此方法。

  1. synchronized修飾靜態(tài)方法
public synchronized static void test1(){
    ....
}
  1. synchronized代碼塊,內(nèi)部使用class
public void test1(){
    synchronized(Test.class){
        ...
    }
}

四、鎖機(jī)制單例中的應(yīng)用

單例對(duì)并發(fā)方面的考慮主要是

  1. 防止創(chuàng)建多個(gè)對(duì)象。單例要求全局只有一個(gè)對(duì)象
  2. 防止單例對(duì)象中的數(shù)據(jù)被并發(fā)修改造成錯(cuò)誤

創(chuàng)建方式

1. 懶漢模式

需要時(shí)再創(chuàng)建

class ConnectionManager{

    private static ConnectionManager sInstance = new ConnectionManager();
    
    private ConnectionManager(){
        
    }
    
    /**
     * 此方式每次獲取都會(huì)加鎖,目的是防止創(chuàng)建多個(gè)對(duì)象,
     * 但創(chuàng)建對(duì)象只有一次,其他都是獲取,造成性能損耗
     */
    public synchronized static ConnectionManager getInstance(){
        if(sInstance==null){
            sInstance=new ConnectionManager();
        }
        return sInstance;
    }
}

2. 餓漢模式

直接初始化(太饑餓,上來就直接吃)

public class ConnectionManager{

    private static ConnectionManager sInstance = new ConnectionManager();
    
    private ConnectionManager(){
    }
    
    public static ConnectionManager getInstance(){
        return sInstance;
    }
} 

3. 雙重檢測(cè)

a. 為什么要2次null判斷?   
    防止兩個(gè)線程同時(shí)進(jìn)入第一個(gè)if模塊,當(dāng)一個(gè)線程獲取鎖后創(chuàng)建一個(gè)對(duì)象;在它釋放鎖后,另一個(gè)線程獲取到鎖,又創(chuàng)建一個(gè)對(duì)象。   
b. 為什么使用volatile修飾?   
    防止指令重排問題。sInstance=new ConnectionManager();這句代碼并不是原子操作,它有三條指令:分配內(nèi)存、初始化內(nèi)存、賦值變量。如果先賦值后初始化,就可能會(huì)出現(xiàn)另一線程在第一個(gè)null判斷時(shí),發(fā)現(xiàn)不為空,直接使用對(duì)象的情況,而此時(shí)對(duì)象可能還沒被初始化造成錯(cuò)誤。
class ConnectionManager{
    private volatile static ConnectionManager sInstance = null;
    private ConnectionManager(){
        
    }
    
     public static ConnectionManager getInstance(){
        if(sInstance==null){
            synchronized(ConnectionManager.class){
                if(sInstance==null){
                    sInstance=new ConnectionManager();
                }
            }
        }
        return sInstance;
    }
}

4. 靜態(tài)內(nèi)部類

class ConnectionManager{

    private ConnectionManager(){
        
    }
    
    public static ConnectionManager getInstance(){
        return Holder.INSTANCE;
    }

    private static class Holder{
        private final static ConnectionManager INSTANCE = new ConnectionManager();
    }
}

5. 枚舉

enum ConnectionManager{
    INSTANCE;
    private ConnectionManager(){
        
    }
}  

五、進(jìn)程通信

  • binder
  • 管道
  • 廣播
  • 文件
  • socket

生產(chǎn)者消費(fèi)者機(jī)制

信息號(hào)機(jī)制

六、線程安全相關(guān)類

容器類

  • ConcurentHashMap
  • CopyOnWriteArrayList
  • CopyOnWriteArraySet
  • LinkedBlockingList
  • LinkedBlockingQueue
  • Vector
  • HashTable
  • Collections.synchronizedList(new ArrayList<T>());
  • Collections.synchronizedSet(new HashSet<T>());
  • Collections.synchronizedMap(new HashMap<String,String>());

字符串類

  • StringBuffer

參考

https://zhuanlan.zhihu.com/p/52563959

?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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