最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺析Java中內(nèi)部鎖和顯示鎖的使用

 更新時間:2023年06月27日 14:43:03   作者:欒還是戀  
在java中鎖主要兩類,內(nèi)部鎖synchronized和顯示鎖java.util.concurrent.locks.Lock,本文將來和大家聊聊這兩者的相關(guān)知識,需要的可以參考一下

java中有哪些鎖

這個問題在我看了一遍<java并發(fā)編程>后盡然無法回答,說明自己對于鎖的概念了解的不夠。于是再次翻看了一下書里的內(nèi)容,突然有點打開腦門的感覺??磥泶_實是要學(xué)習(xí)的最好方式是要帶著問題去學(xué),并且解決問題。

在java中鎖主要兩類:內(nèi)部鎖synchronized和顯示鎖java.util.concurrent.locks.Lock。但細(xì)細(xì)想這貌似總結(jié)的也不太對。應(yīng)該是由java內(nèi)置的鎖和concurrent實現(xiàn)的一系列鎖。

為什么這說,因為在java中一切都是對象,而java對每個對象都內(nèi)置了一個鎖,也可以稱為對象鎖/內(nèi)部鎖。通過synchronized來完成相關(guān)的鎖操作。

而因為synchronized的實現(xiàn)有些缺陷以及并發(fā)場景的復(fù)雜性,有人開發(fā)了一種顯式的鎖,而這些鎖都是由java.util.concurrent.locks.Lock派生出來的。當(dāng)然目前已經(jīng)內(nèi)置到了JDK1.5及之后的版本中。

synchronized

首先來看看用的比較多的synchronized,我的日常工作中大多用的也是它。synchronized是用于為某個代碼塊的提供鎖機制,在java的對象中會隱式的擁有一個鎖,這個鎖被稱為內(nèi)置鎖(intrinsic)或監(jiān)視器鎖(monitor locks)。線程在進入被synchronized保護的塊之前自動獲得這個鎖,直到完成代碼后(也可能是異常)自動釋放鎖。內(nèi)置鎖是互斥的,一個鎖同時只能被一個線程持有,這也就會導(dǎo)致多線程下,鎖被持有后后面的線程會阻塞。正因此實現(xiàn)了對代碼的線程安全保證了原子性。

可重入

既然java內(nèi)置鎖是互斥的而且后面的線程會導(dǎo)致阻塞,那么如果持有鎖的線程再次進入試圖獲得這個鎖時會如何呢?比如下面的一種情況:

public class BaseClass {
    public synchronized void do() {
        System.out.println("is base");
    }
}
public class SonClass extends BaseClass {
    public synchronized void do() {
      System.out.println("is son");
      super.do();
    }
}
SonClass son = new SonClass();
son.do();

此時派生類的do方法除了會首先會持有一次鎖,然后在調(diào)用super.do()的時候又會再一次進入鎖并去持有,如果鎖是互斥的話此時就應(yīng)該死鎖了。

但結(jié)果卻不是這樣的,這是因為內(nèi)部鎖是具有可重入的特性,也就是鎖實現(xiàn)了一個重入機制,引用計數(shù)管理。當(dāng)線程1持有了對象的鎖a,此時會對鎖a的引用計算加1。然后當(dāng)線程1再次獲得鎖a時,線程1還是持有鎖a的那么計算會加1。當(dāng)然每次退出同步塊時會減1,直到為0時釋放鎖。

synchronized的一些特點

修飾代碼的方式

修飾方法

public class BaseClass {
    public synchronized void do() {
        System.out.println("is base");
    }
}

這種就是直接對某個方法進行加鎖,進入這個方法塊時需要獲得鎖。

修飾代碼塊

public class BaseClass {
    private static Object lock = new Object();
    public void do() {
        synchronized (lock) {
            System.out.println("is base");
        }
    }
}

這里就將鎖的范圍減少到了方法中的部分代碼塊,這對于鎖的靈活性就提高了,畢竟鎖的粒度控制也是鎖的一個關(guān)鍵問題。

對象鎖的類型

經(jīng)常看到一些代碼中對synchronized使用比較特別,看一下如下的代碼:

public class BaseClass {
    private static Object lock = new Object();
    public void do() {
        synchronized (lock) {
        }
    }
    public synchronized void doVoid() {
    }
    public synchronized static void doStaticVoid() {
    }
    public  static void doStaticVoid() {
        synchronized (BaseClass.class) {
        }
    }    
}

這里出現(xiàn)了四種情況:修飾代碼塊,修飾了方法,修飾了靜態(tài)方法,修飾BaseClass的class對象。那這幾種情況會有什么不同呢?

修飾代碼塊

這種情況下我們創(chuàng)建了一個對象lock,在代碼中使用synchronized(lock)這種形式,它的意思是使用lock這個對象的內(nèi)置鎖。這種情況下就將鎖的控制交給了一個對象。當(dāng)然這種情況還有一種方式:

public void do() {
    synchronized (this) {
        System.out.println("is base");
    }
}

使用this的意思就是當(dāng)前對象的鎖。這里也道出了內(nèi)置鎖的關(guān)鍵,我提供一把鎖來保護這塊代碼,無論哪個線程來都面對同一把鎖咯。

修飾對象方法

這種直接修飾在方法是咱個情況?其實和修飾代碼塊類似,只不過此時默認(rèn)使用的是this,也就是當(dāng)前對象的鎖。這樣寫起代碼來倒也比較簡單明確。前面說過了與修飾代碼塊的區(qū)別主要還是控制粒度的區(qū)別。

修飾靜態(tài)方法

靜態(tài)方法難道有啥不一樣嗎?確實是不一樣的,此時獲取的鎖已經(jīng)不是this了,而this對象指向的class,也就是類鎖。因為Java中的類信息會加載到方法常量區(qū),全局是唯一的。這其實就提供了一種全局的鎖。

修飾類的Class對象

這種情況其實和修改靜態(tài)方法時比較類似,只不過還是一個道理這種方式可以提供更靈活的控制粒度。

小結(jié)

通過這幾種情況的分析與理解,其實可以看內(nèi)置鎖的主要核心理念就是為一塊代碼提供一個可以用于互斥的鎖,起到類似于開關(guān)的功能。

java中對內(nèi)置鎖也提供了一些實現(xiàn),主要的特點就是java都是對象,而每個對象都有鎖,所以可以根據(jù)情況選擇用什么樣的鎖。

java.util.concurrent.locks.Lock

前面看了synchronized,大部分的情況下差不多就夠啦,但是現(xiàn)在系統(tǒng)在并發(fā)編程中復(fù)雜性是越來越高,所以總是有許多場景synchronized處理起來會比較費勁?;蛘呦?lt;java并發(fā)編程>中說的那樣,concurrent中的lock是對內(nèi)部鎖的一種補充,提供了更多的一些高級特性。

java.util.concurrent.locks.Lock簡單分析

這個接口抽象了鎖的主要操作,也因此讓從Lock派生的鎖具備了這些基本的特性:無條件的、可輪循的、定時的、可中斷的。而且加鎖與解鎖的操作都是顯式進行。下面是它的代碼:

public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

ReentrantLock

ReentrantLock就是可重入鎖,連名字都這么顯式。ReentrantLock提供了和synchronized類似的語義,但是ReentrantLock必須顯式的調(diào)用,比如:

public class BaseClass {
    private Lock lock = new ReentrantLock();
    public void do() {
        lock.lock();
        try {
        //....
        } finally {
          lock.unlock();
        }
    }
}

這種方式對于代碼閱讀來說還是比較清楚的,只不過有個問題,就是如果忘了加try finally或忘 了寫lock.unlock()的話導(dǎo)致鎖沒釋放,很有可能導(dǎo)致一些死鎖的情況,synchronized就沒有這個風(fēng)險。

trylock

ReentrantLock是實現(xiàn)Lock接口,所以自然就擁有它的那些特性,其中就有trylock。trylock就是嘗試獲取鎖,如果鎖已經(jīng)被其他線程占用那么立即返回false,如果沒有那么應(yīng)該占用它并返回true,表示拿到鎖啦。

另一個trylock方法里帶了參數(shù),這個方法的作用是指定一個時間,表示在這個時間內(nèi)一直嘗試去獲得鎖,如果到時間還沒有拿到就放棄。

因為trylock對鎖并不是一直阻塞等待的,所以可以更多的規(guī)避死鎖的發(fā)生。

lockInterruptibly

lockInterruptibly是在線程獲取鎖時優(yōu)先響應(yīng)中斷,如果檢測到中斷拋出中斷異常由上層代碼去處理。這種情況下就為一種輪循的鎖提供了退出機制。為了更好理解可中斷的鎖操作,寫了一個demo來理解。

package com.test;
import java.util.Date;
import java.util.concurrent.locks.ReentrantLock;
public class TestLockInterruptibly {
    static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    doPrint("thread 1 get lock.");
                    do123();
                    doPrint("thread 1 end.");
                } catch (InterruptedException e) {
                    doPrint("thread 1 is interrupted.");
                }
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    doPrint("thread 2 get lock.");
                    do123();
                    doPrint("thread 2 end.");                    
                } catch (InterruptedException e) {
                    doPrint("thread 2 is interrupted.");
                }
            }
        });
        thread1.setName("thread1");
        thread2.setName("thread2");
        thread1.start();
        try {
            Thread.sleep(100);//等待一會使得thread1會在thread2前面執(zhí)行            
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread2.start();
    }
    private static void do123() throws InterruptedException {
        lock.lockInterruptibly();
        doPrint(Thread.currentThread().getName() + " is locked.");
        try {
            doPrint(Thread.currentThread().getName() + " doSoming1....");
            Thread.sleep(5000);//等待幾秒方便查看線程的先后順序
            doPrint(Thread.currentThread().getName() + " doSoming2....");
            doPrint(Thread.currentThread().getName() + " is finished.");
        } finally {
            lock.unlock();
        }
    }
    private static void doPrint(String text) {
        System.out.println((new Date()).toLocaleString() + " : " + text);
    }
}

上面代碼中有兩個線程,thread1比thread2更早啟動,為了能看到拿鎖的過程將上鎖的代碼sleep了5秒鐘,這樣就可以感受到前后兩個線程進入獲取鎖的過程。最終上面的代碼運行結(jié)果如下:

2016-9-28 15:12:56 : thread 1 get lock.
2016-9-28 15:12:56 : thread1 is locked.
2016-9-28 15:12:56 : thread1 doSoming1....
2016-9-28 15:12:56 : thread 2 get lock.
2016-9-28 15:13:01 : thread1 doSoming2....
2016-9-28 15:13:01 : thread1 is finished.
2016-9-28 15:13:01 : thread1 is unloaded.
2016-9-28 15:13:01 : thread2 is locked.
2016-9-28 15:13:01 : thread2 doSoming1....
2016-9-28 15:13:01 : thread 1 end.
2016-9-28 15:13:06 : thread2 doSoming2....
2016-9-28 15:13:06 : thread2 is finished.
2016-9-28 15:13:06 : thread2 is unloaded.
2016-9-28 15:13:06 : thread 2 end.

可以看到,thread1先獲得鎖,一會thread2也來拿鎖,但這個時候thread1已經(jīng)占用了,所以thread2一直到thread1釋放了鎖后才拿到鎖。

**這段代碼說明lockInterruptibly后面來獲取鎖的線程需要等待前面的鎖釋放了才能獲得鎖。**但這里還沒有體現(xiàn)出可中斷的特點,為此增加一些代碼:

thread2.start();
try {
    Thread.sleep(1000);            
} catch (InterruptedException e) {
    e.printStackTrace();
}    
//1秒后把線程2中斷
thread2.interrupt();

在thread2啟動后調(diào)用一下thread2的中斷方法,好吧,先跑一下代碼看看結(jié)果:

2016-9-28 15:16:46 : thread 1 get lock.
2016-9-28 15:16:46 : thread1 is locked.
2016-9-28 15:16:46 : thread1 doSoming1....
2016-9-28 15:16:46 : thread 2 get lock.
2016-9-28 15:16:47 : thread 2 is interrupted. <--直接就響應(yīng)了線程中斷
2016-9-28 15:16:51 : thread1 doSoming2....
2016-9-28 15:16:51 : thread1 is finished.
2016-9-28 15:16:51 : thread1 is unloaded.
2016-9-28 15:16:51 : thread 1 end.

和前面的代碼相比可以發(fā)現(xiàn),thread2正在等待thread1釋放鎖,但是這時thread2自己中斷了,thread2后面的代碼則不會再繼續(xù)執(zhí)行。

ReadWriteLock

顧名思義就是讀寫鎖,這種讀-寫鎖的應(yīng)用場景可以這樣理解,比如一波數(shù)據(jù)大部分時候都是提供讀取的,而只有比較少量的寫操作,那么如果用互斥鎖的話就會導(dǎo)致線程間的鎖競爭。如果對于讀取的時候大家都可以讀,一旦要寫入的時候就再將某個資源鎖住。這樣的變化就很好的解決了這個問題,使的讀操作可以提高讀的性能,又不會影響寫的操作。

一個資源可以被多個讀者訪問,或者被一個寫者訪問,兩者不能同時進行。

這是讀寫鎖的抽象接口,定義一個讀鎖和一個寫鎖。

public interface ReadWriteLock {
    /**
     * Returns the lock used for reading.
     *
     * @return the lock used for reading
     */
    Lock readLock();
    /**
     * Returns the lock used for writing.
     *
     * @return the lock used for writing
     */
    Lock writeLock();
}

在JDK里有個ReentrantReadWriteLock實現(xiàn),就是可重入的讀-寫鎖。ReentrantReadWriteLock可以構(gòu)造為公平的或者非公平的兩種類型。如果在構(gòu)造時不顯式指定則會默認(rèn)的創(chuàng)建非公平鎖。在非公平鎖的模式下,線程訪問的順序是不確定的,就是可以闖入;可以由寫者降級為讀者,但是讀者不能升級為寫者。

如果是公平鎖模式,那么選擇權(quán)交給等待時間最長的線程,如果一個讀線程獲得鎖,此時一個寫線程請求寫入鎖,那么就不再接收讀鎖的獲取,直到寫入操作完成。

簡單的代碼分析 在ReentrantReadWriteLock里其實維護的是一個sync的鎖,只是看起來語義上像是一個讀鎖和寫鎖??匆幌滤臉?gòu)造函數(shù):

public ReentrantReadWriteLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
    readerLock = new ReadLock(this);
    writerLock = new WriteLock(this);
}
//讀鎖的構(gòu)造函數(shù)
protected ReadLock(ReentrantReadWriteLock lock) {
    sync = lock.sync;
}
//寫鎖的構(gòu)造函數(shù)
protected WriteLock(ReentrantReadWriteLock lock) {
    sync = lock.sync;
}

可以看到實際上讀/寫鎖在構(gòu)造時都是引用的ReentrantReadWriteLock的sync鎖對象。而這個Sync類是ReentrantReadWriteLock的一個內(nèi)部類??傊x/寫鎖都是通過Sync來完成的。它是如何來協(xié)作這兩者關(guān)系呢?

//讀鎖的加鎖方法
public void lock() {
    sync.acquireShared(1);
}
//寫鎖的加鎖方法
public void lock() {
    sync.acquire(1);
}

區(qū)別主要是讀鎖獲得的是共享鎖,而寫鎖獲取的是獨占鎖。這里有個點可以提一下,就是ReentrantReadWriteLock為了保證可重入性,共享鎖和獨占鎖都必須支持持有計數(shù)和重入數(shù)。而ReentrantLock是使用state來存儲的,而state只能存一個整形值,為了兼容兩個鎖的問題,所以將其劃分了高16位和低16位分別存共享鎖的線程數(shù)量或獨占鎖的線程數(shù)量或者重入計數(shù)。

以上就是淺析Java中內(nèi)部鎖和顯示鎖的使用的詳細(xì)內(nèi)容,更多關(guān)于Java鎖的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring中的ThreadPoolTaskExecutor線程池使用詳解

    Spring中的ThreadPoolTaskExecutor線程池使用詳解

    這篇文章主要介紹了Spring中的ThreadPoolTaskExecutor線程池使用詳解,ThreadPoolTaskExecutor 是 Spring框架提供的一個線程池實現(xiàn),用于管理和執(zhí)行多線程任務(wù),它是TaskExecutor接口的實現(xiàn),提供了在 Spring 應(yīng)用程序中創(chuàng)建和配置線程池的便捷方式,需要的朋友可以參考下
    2024-01-01
  • 有關(guān)tomcat內(nèi)存溢出的完美解決方法

    有關(guān)tomcat內(nèi)存溢出的完美解決方法

    下面小編就為大家?guī)硪黄嘘P(guān)tomcat內(nèi)存溢出的完美解決方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-05-05
  • 詳解springboot解決CORS跨域的三種方式

    詳解springboot解決CORS跨域的三種方式

    本文主要介紹了詳解springboot解決CORS跨域的三種方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • BufferedReader中read()方法和readLine()方法的使用

    BufferedReader中read()方法和readLine()方法的使用

    這篇文章主要介紹了BufferedReader中read()方法和readLine()方法的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • 基于Spring整合mybatis注解掃描是否成功的問題

    基于Spring整合mybatis注解掃描是否成功的問題

    這篇文章主要介紹了Spring整合mybatis注解掃描是否成功的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Maven項目打包時添加本地Jar包的操作步驟

    Maven項目打包時添加本地Jar包的操作步驟

    在 Maven 項目開發(fā)中,我們經(jīng)常會遇到需要引入本地 Jar 包的場景,比如使用未發(fā)布到中央倉庫的第三方庫或者處理版本沖突的依賴項,本文將詳細(xì)介紹如何通過 Maven 命令將本地 Jar 包安裝到本地倉庫,并在項目中正確引用,需要的朋友可以參考下
    2025-05-05
  • Java?OkHttp框架源碼超詳細(xì)解析

    Java?OkHttp框架源碼超詳細(xì)解析

    okhttp是一個第三方類庫,用于android中請求網(wǎng)絡(luò)。這是一個開源項目,是安卓端最火熱的輕量級框架,由移動支付Square公司貢獻(該公司還貢獻了Picasso和LeakCanary)?。用于替代HttpUrlConnection和Apache?HttpClient
    2022-11-11
  • JDBC連接MySQL并實現(xiàn)模糊查詢

    JDBC連接MySQL并實現(xiàn)模糊查詢

    本文詳細(xì)講解了JDBC連接MySQL并實現(xiàn)模糊查詢的方式,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-01-01
  • idea 隱藏target,iml等不需要展示的文件(推薦)

    idea 隱藏target,iml等不需要展示的文件(推薦)

    這篇文章主要介紹了idea 隱藏target,iml等不需要展示的文件,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • JAVA中Collections.sort()方法使用詳解

    JAVA中Collections.sort()方法使用詳解

    這篇文章主要給大家介紹了關(guān)于JAVA中Collections.sort()方法使用的相關(guān)資料,Java中Collections.sort()方法是用來對List類型進行排序的,文中通過代碼將使用的方法介紹的非常詳細(xì),需要的朋友可以參考下
    2024-05-05

最新評論

武清区| 珲春市| 平安县| 宁远县| 宜兰县| 东宁县| 汪清县| 泰来县| 铁岭县| 友谊县| 军事| 项城市| 高州市| 长武县| 南昌县| 巫溪县| 惠来县| 高陵县| 博罗县| 邻水| 崇阳县| 金湖县| 图木舒克市| 张家界市| 阿克苏市| 通渭县| 宁陵县| 阳春市| 翁源县| 朝阳区| 綦江县| 临猗县| 博罗县| 天镇县| 蒙山县| 准格尔旗| 龙陵县| 海城市| 黄平县| 奉化市| 那坡县|