【Java鎖】(公平鎖、非公平鎖、可重入鎖、遞歸鎖、自旋鎖)談?wù)勀愕睦斫??手?xiě)一個(gè)自旋鎖

Java有很多種鎖:公平鎖、非公平鎖、可重入鎖、遞歸鎖、自旋鎖、讀鎖、寫(xiě)鎖、等等

公平和非公平鎖

java.util.concurrent.locks.ReentrantLock可以通過(guò)指定構(gòu)造函數(shù)的boolean類(lèi)型來(lái)得到公平鎖或者非公平鎖,默認(rèn)情況下將構(gòu)造非公平鎖。

是什么

公平鎖:是指多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲取鎖,類(lèi)似于排隊(duì),先來(lái)后到。
非公平鎖:是指多個(gè)線程獲取鎖的順序并不是按照申請(qǐng)鎖的順序,有可能后申請(qǐng)的線程比先申請(qǐng)的線程更有先獲取鎖,在高并發(fā)的情況下,有可能會(huì)造成優(yōu)先級(jí)反轉(zhuǎn)或者饑餓現(xiàn)象。

//Creates an instance of ReentrantLock. This is equivalent to using ReentrantLock(false).
    public ReentrantLock() {
        sync = new NonfairSync();

構(gòu)造方法有兩個(gè):
ReentrantLock()
ReentrantLock(boolean fair)
根據(jù)給定的公平政策創(chuàng)建一個(gè) ReentrantLock的實(shí)例。(默認(rèn)情況下創(chuàng)建的為非公平鎖)

兩者之間的區(qū)別

公平鎖:并發(fā)環(huán)境下,每個(gè)線程獲取鎖時(shí)會(huì)先查看此鎖維護(hù)的等待隊(duì)列,如果為空,或者當(dāng)前線程是等待隊(duì)列的一個(gè),就占有鎖,否則就睡加入到等待隊(duì)列中,以后會(huì)按照FIFO規(guī)則從隊(duì)列中取到自己。
非公平鎖:比較粗魯,一開(kāi)始就直接嘗試占有鎖,如果嘗試失敗,再采用類(lèi)似公平鎖的方式。

  • 非公平鎖的優(yōu)點(diǎn)在于吞吐量比公平鎖大
  • 對(duì)于Synchronized而言,也是非公平鎖。
    -------------------------------------------(分割線)--------------------------------------------------

可重入鎖(又名遞歸鎖)(ReentrantLock)

是什么

可重入鎖,也叫遞歸鎖。指的是

  • 同一線程外層函數(shù)獲得鎖之后,內(nèi)層遞歸函數(shù)仍能獲得該鎖的代碼。
  • 在同一個(gè)線程在外層方法獲取鎖的時(shí)候,再進(jìn)入內(nèi)層方法會(huì)自動(dòng)獲取鎖。
    即: 線程可以進(jìn)入任何一個(gè)他已經(jīng)擁有的鎖所同步著的代碼塊。
    //外層函數(shù)
    public synchronized void method01(){
        method02();//M2的鎖由于M1 獲得了鎖而自然獲得
    }
    //內(nèi)層函數(shù)
    public synchronized void method02(){}

ReentrantLock/Synchronize都是典型的可重入鎖(默認(rèn)非公平)

可重入鎖最大的作用就是避免死鎖

死鎖,M1獲得了鎖,調(diào)用M2,M2一直等待M1結(jié)束,M1會(huì)死鎖
一系列流程使用同一個(gè)鎖,執(zhí)行方法1時(shí)候鎖住,執(zhí)行方法2時(shí)需要等待自己在方法1 中獲得的鎖解掉,形成了自己需要獲得鎖,自己又需要解掉鎖的死鎖情況。

可重入鎖的種類(lèi)

  • 隱式鎖(即Synchronized關(guān)鍵字使用的鎖)默認(rèn)是可重入鎖
  • 顯式鎖(即Lock)也有ReentrantLock這樣的可重入鎖

ReentrantLockDemo

class Phone implements Runnable
{
    public synchronized void sendSMS()throws Exception{
        System.out.println(Thread.currentThread().getName()+"\t-----------invoked sendSMS");
        sendEmail();
    }
    public synchronized void sendEmail()throws Exception{
        System.out.println(Thread.currentThread().getName()+"\t+++++++++++invoked sendEmail");
    }
    //----------------------------------------------------------------------
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        get();
    }
    private void get(){
        //加鎖要和解鎖個(gè)數(shù)配對(duì)?。?編譯運(yùn)行都不會(huì)報(bào)錯(cuò),會(huì)有鎖滯留)
        lock.lock();
        try{
            System.out.println(Thread.currentThread().getName()+"\t-----------invoked get");
            set();
        }finally {
            //加鎖要和解鎖個(gè)數(shù)配對(duì)?。?編譯運(yùn)行都不會(huì)報(bào)錯(cuò),會(huì)有鎖滯留)
            lock.unlock();
        }
    }
    private void set() {
        lock.lock();
        try{
            System.out.println(Thread.currentThread().getName()+"\t+++++++++++invoked set");
        }finally {
            lock.unlock();
        }
    }
}
public class ReentrantLockDemo {
    public static void main(String[] args) {
        System.out.println("-----------------------Synchronized");
        Phone phone = new Phone();
        new Thread(()->{
            try {
                phone.sendSMS();
            }catch (Exception e){
                e.printStackTrace();
            }
        },"t1").start();
        new Thread(()->{
            try {
                phone.sendSMS();
            }catch (Exception e){
                e.printStackTrace();
            }
        },"t2").start();

        try{TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
        System.out.println("-----------------------ReentrantLock");
        new Thread(()->{
            phone.run();
        },"t3").start();
        new Thread(()->{
            phone.run();
        },"t4").start();
    }
}

Console:
-----------------------Synchronized
t1  -----------invoked sendSMS
t1  +++++++++++invoked sendEmail
t2  -----------invoked sendSMS
t2  +++++++++++invoked sendEmail
-----------------------ReentrantLock
t3  -----------invoked get
t3  +++++++++++invoked set
t4  -----------invoked get
t4  +++++++++++invoked set

加鎖要和解鎖個(gè)數(shù)配對(duì)!!(編譯運(yùn)行都不會(huì)報(bào)錯(cuò),會(huì)有鎖滯留)

自旋鎖

是指嘗試獲取鎖的線程不會(huì)立即阻塞,而是采用循環(huán)的方式取嘗試獲取鎖,

  • 這樣做的好處是減少線程上下文切換的消耗
  • 缺點(diǎn)是循環(huán)會(huì)消耗CPU。
  • 循環(huán)比較獲取,直到成功為止,沒(méi)有類(lèi)似wait的阻塞。
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));
        //預(yù)期值和主物理內(nèi)存中的值不一致就一直重新取(可能會(huì)出現(xiàn)多次循環(huán))
        return var5;
    }

手寫(xiě)一個(gè)自旋鎖

package com.company;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

public class SpinLockDemo {
    //原子引用線程
    AtomicReference<Thread> atomicReference = new AtomicReference<>();
    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName()+"  came in for the Lock");
        while (!atomicReference.compareAndSet(null,thread)){
        //空循環(huán)-> 直到atomicReference為null時(shí),執(zhí)行CAS,并跳出循環(huán)
        }
    }
    public void unLock(){
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread,null);
        System.out.println(thread.getName()+"  release the Lock");
    }
    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        new Thread(()->{
            spinLockDemo.myLock();
            System.out.println("Thread A get the Lock");
            try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}
            spinLockDemo.unLock();
        },"A").start();
        try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
        new Thread(()->{
            spinLockDemo.myLock();
            System.out.println("Thread B get the Lock");
            spinLockDemo.unLock();
        },"B").start();
    }
}

----------------------------------------------------------------------------------------------------
Console:
                     A  came in for the Lock
                     Thread A get the Lock
                     B  came in for the Lock
                             (5S后)
                     A  release the Lock
                     Thread B get the Lock
                     B  release the Lock
最后編輯于
?著作權(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)容

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