亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

程序猿必須要掌握的多線程安全問題之鎖策略詳解

 更新時(shí)間:2021年06月01日 14:47:43   作者:沉默著忍受  
在筆者面試過程時(shí),經(jīng)常會(huì)被問到各種各樣的鎖,如樂觀鎖、讀寫鎖等等,非常繁多,在此做一個(gè)總結(jié),介紹的內(nèi)容如下,需要的朋友可以參考下

一、常見的鎖策略

1.1 樂觀鎖

樂觀鎖:樂觀鎖假設(shè)認(rèn)為數(shù)據(jù)一般情況下不會(huì)產(chǎn)生并發(fā)沖突,所以在數(shù)據(jù)進(jìn)行提交更新的時(shí)候,才會(huì)正 式對數(shù)據(jù)是否產(chǎn)生并發(fā)沖突進(jìn)行檢測,如果發(fā)現(xiàn)并發(fā)沖突了,則讓返回用戶錯(cuò)誤的信息,讓用戶決定如 何去做。樂觀鎖的性能比較高。
悲觀鎖:總是假設(shè)最壞的情況,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人會(huì)修改,所以每次在拿數(shù)據(jù)的時(shí)候都會(huì) 上鎖,這樣別人想拿這個(gè)數(shù)據(jù)就會(huì)阻塞直到它拿到鎖。
悲觀鎖的問題:總是需要競爭鎖,進(jìn)而導(dǎo)致發(fā)生線程切換,掛起其他線程;所以性能不高。 樂觀鎖的問題:并不總是能處理所有問題,所以會(huì)引入一定的系統(tǒng)復(fù)雜度。

樂觀鎖的使用場景:

import java.util.concurrent.atomic.AtomicInteger;

public class happylock {
    private  static  AtomicInteger count = new AtomicInteger(0);
    private  static  final  int MAXSIZE = 100000;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0;i<MAXSIZE;i++){
                    count.getAndIncrement();
                }
            }
        });
        t1.start();
        t1.join();
        Thread t2= new Thread(new Runnable() {
            @Override
            public void run() {
                for(int j = 0;j<MAXSIZE;j++){
                    count.getAndDecrement();
                }
            }
        });
        t2.start();
        t2.join();
        System.out.println("結(jié)果"+count);
    }
//結(jié)果是0,如果不加AtomicInteger,那么線程執(zhí)行完以后不會(huì)是0,存在線程不安全!

}

1.2 悲觀鎖

悲觀鎖:他認(rèn)為通常情況下會(huì)出現(xiàn)并發(fā)沖突,所以它在一開始就加鎖;
synchronized 就是悲觀鎖

1.3 讀寫鎖

多線程之間,數(shù)據(jù)的讀取方之間不會(huì)產(chǎn)生線程安全問題,但數(shù)據(jù)的寫入方互相之間以及和讀者之間都需  要進(jìn)行互斥。如果兩種場景下都用同一個(gè)鎖,
就會(huì)產(chǎn)生極大的性能損耗。所以讀寫鎖因此而產(chǎn)生。

讀寫鎖(readers-writer lock),看英文可以顧名思義,在執(zhí)行加鎖操作時(shí)需要額外表明讀寫意圖,復(fù)數(shù)讀者之間并不互斥,而寫者則要求與任何人互斥。
把鎖分成兩個(gè)鎖,一個(gè)是讀鎖,一個(gè)是寫鎖,其中讀鎖可以多個(gè)線程擁有,而寫鎖是一個(gè)線程擁有。讀鎖是共享鎖,而寫鎖是非公享鎖。
讀寫鎖的應(yīng)用方法:

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Readerlock {
    //讀寫鎖的具體實(shí)現(xiàn)
    public static void main(String[] args) {
        //創(chuàng)建讀寫鎖
        ReentrantReadWriteLock  reentrantReadWriteLock = new ReentrantReadWriteLock();
        //分離讀鎖
        ReentrantReadWriteLock.ReadLock readLock=  ReadWriteLock.ReadLock();
        //分離寫鎖
        ReentrantReadWriteLock.WriteLock readLock=  ReadWriteLock.WriteLock();
    }

}

1.4 公平鎖與非公平鎖

公平鎖:鎖的獲取順序必須合線程方法的先后順序是保存一致的,就叫公平鎖 優(yōu)點(diǎn):執(zhí)行時(shí)順序的,所以結(jié)果是可以預(yù)期的
非公平鎖:鎖的獲取方式循序和線程獲取鎖的順序無關(guān)。優(yōu)點(diǎn):性能比較高

1.5 自旋鎖(Spin Lock)

按之間的方式處理下,線程在搶鎖失敗后進(jìn)入阻塞狀態(tài),放棄 CPU,需要過很久才能再次被調(diào)度。但經(jīng)過測算,實(shí)際的生活中,大部分情況下,雖然當(dāng)前搶鎖失敗,但過不了很久,鎖就會(huì)被釋放?;谶@個(gè) 事實(shí),自旋鎖誕生了。
你可以簡單的認(rèn)為自旋鎖就是下面的代碼

只要沒搶到鎖,就死等。

自旋鎖的缺點(diǎn):
缺點(diǎn)其實(shí)非常明顯,就是如果之前的假設(shè)(鎖很快會(huì)被釋放)沒有滿足,則線程其實(shí)是光在消耗 CPU 資源,長期在做無用功的。

1.6 可重入鎖

可重入鎖的字面意思是“可以重新進(jìn)入的鎖”,即允許同一個(gè)線程多次獲取同一把鎖。比如一個(gè)遞歸函數(shù) 里有加鎖操作,遞歸過程中這個(gè)鎖會(huì)阻塞自己嗎?如果不會(huì),那么這個(gè)鎖就是可重入鎖(因?yàn)檫@個(gè)原因 可重入鎖也叫做遞歸鎖)。
Java里只要以Reentrant開頭命名的鎖都是可重入鎖,而且JDK提供的所有現(xiàn)成的Lock實(shí)現(xiàn)類,包括
synchronized關(guān)鍵字鎖都是可重入的。

1.7 相關(guān)題目

面試題:

1.你是怎么理解樂觀鎖和悲觀鎖的,具體怎么實(shí)現(xiàn)呢?

樂觀鎖——> CAS ——> Atomic.(CAS是由v(內(nèi)存值) A(預(yù)期值)B(新值))組成,然后執(zhí)行的時(shí)候是使用V=A對比,如果結(jié)果為true,這表明沒有并發(fā)沖突,則可以直接進(jìn)行修改,否則返回錯(cuò)誤信息。*

2.有了解什么讀寫鎖么?

多線程之間,數(shù)據(jù)的讀取方之間不會(huì)產(chǎn)生線程安全問題,但數(shù)據(jù)的寫入方互相之間以及和讀者之間都需 要進(jìn)行互斥。如果兩種場景下都用同一個(gè)鎖,就會(huì)產(chǎn)生極大的性能損耗。所以讀寫鎖因此而產(chǎn)生。
讀寫鎖(readers-writer lock),看英文可以顧名思義,在執(zhí)行加鎖操作時(shí)需要額外表明讀寫意圖,復(fù)數(shù)讀者之間并不互斥,而寫者則要求與任何人互斥。
把鎖分成兩個(gè)鎖,一個(gè)是讀鎖,一個(gè)是寫鎖,其中讀鎖可以多個(gè)線程擁有,而寫鎖是一個(gè)線程擁有

3.什么是自旋鎖,為什么要使用自旋鎖策略呢,缺點(diǎn)是什么?

按之間的方式處理下,線程在搶鎖失敗后進(jìn)入阻塞狀態(tài),放棄 CPU,需要過很久才能再次被調(diào)度。但經(jīng)過測算,實(shí)際的生活中,大部分情況下,雖然當(dāng)前搶鎖失敗,但過不了很久,鎖就會(huì)被釋放?;谶@個(gè) 事實(shí),自旋鎖誕生了。
你可以簡單的認(rèn)為自旋鎖就是下面的代碼
只要沒搶到鎖,就死等。
自旋鎖的缺點(diǎn):
缺點(diǎn)其實(shí)非常明顯,就是如果之前的假設(shè)(鎖很快會(huì)被釋放)沒有滿足,則線程其實(shí)是光在消耗 CPU 資源,長期在做無用功的。

4.synchronized 是可重入鎖么?

synchronized 是可重入鎖,

代碼如下:

public class Chonglock {
    private  static   Object lock = new Object();

    public static void main(String[] args) {
        //第一次進(jìn)入鎖
        synchronized (lock){
            System.out.println("第一次進(jìn)入鎖");
            synchronized (lock){
                System.out.println("第二次進(jìn)入鎖");
            }
        }
    }
}

二、CAS問題

2.1 什么是CAS問題

CAS: 全稱Compare and swap,字面意思:”比較并交換“,一個(gè) CAS 涉及到以下操作:
我們假設(shè)內(nèi)存中的原數(shù)據(jù)V,舊的預(yù)期值A(chǔ),需要修改的新值B。 1. 比較 A 與 V 是否相等。(比較) 2. 如果比較相等,將 B 寫入 V。(交換) 3. 返回操作是否成功。
當(dāng)多個(gè)線程同時(shí)對某個(gè)資源進(jìn)行CAS操作,只能有一個(gè)線程操作成功,但是并不會(huì)阻塞其他線程,其他線程只會(huì)收到操作失敗的信號(hào)??梢?CAS 其實(shí)是一個(gè)樂觀鎖。

2.2 CAS 是怎么實(shí)現(xiàn)的

針對不同的操作系統(tǒng),JVM 用到了不同的 CAS 實(shí)現(xiàn)原理,簡單來講:
java 的 CAS 利用的的是 unsafe 這個(gè)類提供的 CAS 操作;
unsafe 的 CAS 依 賴 了 的 是 jvm 針 對 不 同 的 操 作 系 統(tǒng) 實(shí) 現(xiàn) 的 Atomic::cmpxchg(一個(gè)原子性的指令)

/Atomic::cmpxchg 的實(shí)現(xiàn)使用了匯編的 CAS 操作,并使用 cpu 硬件提供的 lock 機(jī)制保證其原子性。
簡而言之,是因?yàn)橛布枰粤酥С?,軟件層面才能做到?/p>

在這里插入圖片描述

2.3 CAS 有哪些應(yīng)用

2.3.1 實(shí)現(xiàn)自旋鎖

public class SpinLock {
private AtomicReference<Thread> sign =new AtomicReference<>();

public void lock(){
Thread current = Thread.currentThread();

// 不放棄 CPU,一直在這里旋轉(zhuǎn)判斷
while(!sign .compareAndSet(null, current)){
}
}

public void unlock (){
Thread current = Thread.currentThread(); sign.compareAndSet(current, null);
}
}

用于實(shí)現(xiàn)原子類

示例代碼:

public class AtomicInteger {
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
}

public class Unsafe {
public final int getAndAddInt(Object var1, long var2, int var4) { int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

return var5;
}
}

三、ABA問題

3.1 什么是ABA問題

ABA 的問題,就是一個(gè)值從A變成了B又變成了A,而這個(gè)期間我們不清楚這個(gè)過程。

3.2 實(shí)現(xiàn)ABA問題場景

我來舉一個(gè)例子,如果你向別人轉(zhuǎn)錢,你需要轉(zhuǎn)100元,但是你點(diǎn)擊了兩次轉(zhuǎn)錢,第一次會(huì)成功,但是第二次肯定會(huì)失敗,但是,在你點(diǎn)擊第二次轉(zhuǎn)錢的同一時(shí)刻,你的公司給你轉(zhuǎn)了100元工資,那么你就會(huì)莫名其妙的把100又轉(zhuǎn)了出去,你丟失了100,別人也沒有獲得100.
代碼演示:

1.正常轉(zhuǎn)錢流程

import java.util.concurrent.atomic.AtomicReference;

public class Aba {
    //ABA問題的演示

    private  static AtomicReference money = new AtomicReference(100);//轉(zhuǎn)賬


    public static void main(String[] args) {
        //轉(zhuǎn)賬線程1
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
               boolean result =  money.compareAndSet(100,0);
                System.out.println("點(diǎn)擊第一次轉(zhuǎn)出100"+result);
            }
        });
        t1.start();
        //轉(zhuǎn)賬線程2
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
               boolean result =  money.compareAndSet(100,0);
                System.out.println("點(diǎn)擊第二次轉(zhuǎn)出100"+result);
                if(!result){
                    System.out.println("余額不足,無法轉(zhuǎn)賬!");
                }
            }
        });
        t2.start();


    }
}

在這里插入圖片描述

2.錯(cuò)誤操作后:

import java.util.concurrent.atomic.AtomicReference;

public class ABas {

    private  static AtomicReference money = new AtomicReference(100);//轉(zhuǎn)賬


    public static void main(String[] args) throws InterruptedException {
        //轉(zhuǎn)賬出線程1
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                boolean result =  money.compareAndSet(100,0);
                System.out.println("第一次"+result);
            }
        });
        t1.start();
        t1.join();
        //轉(zhuǎn)入100
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                boolean result =  money.compareAndSet(0,100);
                System.out.println("轉(zhuǎn)賬"+result);
            }
        });
        t3.start();
        //轉(zhuǎn)賬線程2
        t3.join();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                boolean result =  money.compareAndSet(100,0);
                System.out.println("第二次"+result);
            }
        });
        t2.start();


    }
}

在這里插入圖片描述

解決ABA方法

解決方法:加入版本信息,例如攜帶 AtomicStampedReference 之類的時(shí)間戳作為版本信息,保證不會(huì)
出現(xiàn)老的值。

代碼實(shí)現(xiàn):

import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

public class Abaack {

    //private  static AtomicReference money = new AtomicReference(100);//轉(zhuǎn)賬
private  static AtomicStampedReference money = new AtomicStampedReference(100,1);


//
    public static void main(String[] args) throws InterruptedException {
        //轉(zhuǎn)賬出線程1
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                boolean result =  money.compareAndSet(100,0,1,2);
                System.out.println("第一次轉(zhuǎn)賬100:"+result);
            }
        });
        t1.start();
        t1.join();
        //轉(zhuǎn)入100
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                boolean result =  money.compareAndSet(0,100,2,3);
                System.out.println("其他人給你轉(zhuǎn)賬了100:"+result);
            }
        });
        t3.start();
        //轉(zhuǎn)賬線程2
        t3.join();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                boolean result =  money.compareAndSet(100,0,1,2);
                System.out.println("第二次點(diǎn)擊轉(zhuǎn)賬100:"+result);
            }
        });
        t2.start();

//Integer的高速緩存是-128--127(AtomicStampedReference)
        //如果大于127,那么就開始new對象了
        /*
        * 解決方法,調(diào)整邊界值*/
    }


}

在這里插入圖片描述

四、總結(jié)

以上就是今天要講的內(nèi)容,本文僅僅簡單介紹了鎖策略,解決線程安全。

到此這篇關(guān)于程序猿必須要掌握的多線程安全問題之鎖策略詳解的文章就介紹到這了,更多相關(guān)java鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論