<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          多線程與高并發(fā)干貨筆記分享,造起來(lái)

          共 9937字,需瀏覽 20分鐘

           ·

          2020-09-20 19:49

          1. 創(chuàng)建線程的四種方式

          • 實(shí)現(xiàn)Runnable 重寫(xiě)run方法

          • 繼承Thread 重寫(xiě)run方法

          • 線程池創(chuàng)建 Executors.newCachedThreadPool()

          • 實(shí)現(xiàn)Callable接口

          2. Thread線程操作方法

          當(dāng)前線程睡眠指定mills毫秒

          • Thread.sleep([mills])

            當(dāng)前線程優(yōu)雅讓出執(zhí)行權(quán)

          • Thread.yield()

          ????????例如Thread t1, t2,在t2的run方法中調(diào)用t1.join(),線程t2將等待t1完成后執(zhí)行

          • join

          3. Thread狀態(tài)

          狀態(tài)使用場(chǎng)景
          NEWThread被創(chuàng)建之后,未start之前
          RUNNABLE在調(diào)用start()方法之后,這也是線程進(jìn)入運(yùn)行狀態(tài)的唯一一種方式。
          具體分為ready跟running,當(dāng)線程被掛起或者調(diào)用Thread.yield()的時(shí)候?yàn)閞eady
          WAITING當(dāng)一個(gè)線程執(zhí)行了Object.wait()的時(shí)候,它一定在等待另一個(gè)線程執(zhí)行Object.notify()或者Object.notifyAll()。
          或者一個(gè)線程thread,其在主線程中被執(zhí)行了thread.join()的時(shí)候,主線程即會(huì)等待該線程執(zhí)行完成。當(dāng)一個(gè)線程執(zhí)行了LockSupport.park()的時(shí)候,其在等待執(zhí)行LockSupport.unpark(thread)。當(dāng)該線程處于這種等待的時(shí)候,其狀態(tài)即為WAITING。需要關(guān)注的是,這邊的等待是沒(méi)有時(shí)間限制的,當(dāng)發(fā)現(xiàn)有這種狀態(tài)的線程的時(shí)候,若其長(zhǎng)時(shí)間處于這種狀態(tài),也需要關(guān)注下程序內(nèi)部有無(wú)邏輯異常。

          TIMED_WAITING

          這個(gè)狀態(tài)和WAITING狀態(tài)的區(qū)別就是,這個(gè)狀態(tài)的等待是有一定時(shí)效的
          Thread.sleep(long)
          Object.wait(long)
          Thread.join(long)
          LockSupport.parkNanos()
          LockSupport.parkUntil()
          BLOCKED在進(jìn)入synchronized關(guān)鍵字修飾的方法或代碼塊(獲取鎖)時(shí)的狀態(tài)
          TERMINATED線程執(zhí)行結(jié)束之后的狀態(tài)。
          線程一旦終止了,就不能復(fù)生。
          在一個(gè)終止的線程上調(diào)用start()方法,會(huì)拋出java.lang.IllegalThreadStateException異常

          4. synchronized

          • 鎖住的是對(duì)象而不是代碼

          • this 等價(jià)于 當(dāng)前類(lèi).class

          • 鎖定方法,非鎖定方法同時(shí)進(jìn)行

          • 鎖在執(zhí)行過(guò)程中發(fā)生異常會(huì)自動(dòng)釋放鎖

          • synchronized獲得的鎖是可重入的

          • 鎖升級(jí) 偏向鎖-自旋鎖-重量級(jí)鎖

          • synchronized(object)不能用String常量/Integer,Long等基本數(shù)據(jù)類(lèi)型

          • 鎖定對(duì)象的時(shí)候要保證對(duì)象不能被重寫(xiě),最好加final定義

          4. volatile

          • 保證線程可見(jiàn)性

          • 禁止指令重排序

          • volatile并不能保證多個(gè)線程修改的一致性,要保持一致性還是需要synchronized關(guān)鍵字

          • volatile 引用類(lèi)型(包括數(shù)組)只能保證引用本身的可見(jiàn)性,不能保證內(nèi)部字段的可見(jiàn)性 volatile關(guān) 鍵字只能用于變量而不可以修飾方法以及代碼塊

          5. synchronized與AtomicLong以及LongAdder的效率對(duì)比

          Synchronized 是需要加鎖的,效率偏低;AtomicLong 不需要申請(qǐng)鎖,使用CAS機(jī)制;LongAdder 使用分段鎖,所以效率好,在并發(fā)數(shù)量特別高的時(shí)候,LongAdder最合適

          6. ConcurrentHashMap的分段鎖原理

          分段鎖就是將數(shù)據(jù)分段上鎖,把鎖進(jìn)一步細(xì)粒度化,有助于提升并發(fā)效率。HashTable容器在競(jìng)爭(zhēng)激烈的并發(fā)環(huán)境下表現(xiàn)出效率低下的原因是所有訪問(wèn)HashTable的線程都必須競(jìng)爭(zhēng)同一把鎖,假如容器里有多把鎖,每一把鎖用于鎖容器其中一部分?jǐn)?shù)據(jù),那么當(dāng)多線程訪問(wèn)容器里不同數(shù)據(jù)段的數(shù)據(jù)時(shí),線程間就不會(huì)存在鎖競(jìng)爭(zhēng),從而可以有效提高并發(fā)訪問(wèn)效率,這就是ConcurrentHashMap所使用的鎖分段技術(shù)。首先將數(shù)據(jù)分成一段一段地存儲(chǔ),然后給每一段數(shù)據(jù)配一把鎖,當(dāng)一個(gè)線程占用鎖訪問(wèn)其中一個(gè)段數(shù)據(jù)的時(shí)候,其他段的數(shù)據(jù)也能被其他線程訪問(wèn)。

          7. ReentrantLock

          ReentrantLock可以替代synchronized 但是ReentrantLock必須手動(dòng)開(kāi)啟鎖/關(guān)閉鎖,synchronized遇到異常會(huì)自動(dòng)釋放鎖,ReentrantLock需要手動(dòng)關(guān)閉,一般都是放在finally中關(guān)閉 定義鎖 Lock lock = new ReentrantLock(); 開(kāi)啟 lock.lock(); 關(guān)閉 lock.unlock(); 使用Reentrantlock可以進(jìn)行“嘗試鎖定”tryLock,這樣無(wú)法鎖定,或者在指定時(shí)間內(nèi)無(wú)法鎖定,線程可以決定是否繼續(xù)等待。使用tryLock進(jìn)行嘗試鎖定,不管鎖定與否,方法都將繼續(xù)執(zhí)行 可以根據(jù)tryLock的返回值來(lái)判定是否鎖定 也可以指定tryLock的時(shí)間,由于tryLock(time)拋出異常,所以要注意unclock的處理,必須放到finally中,如果tryLock未鎖定,則不需要unlock 使用ReentrantLock還可以調(diào)用lockInterruptibly方法,可以對(duì)線程interrupt方法做出響應(yīng),在一個(gè)線程等待鎖的過(guò)程中,可以被打斷 new ReentrantLock(true) 表示公平鎖,不帶參數(shù)默認(rèn)為false,非公平鎖

          8. CountDownLatch

          countDownLatch這個(gè)類(lèi)可以使一個(gè)線程等待其他線程各自執(zhí)行完畢后再執(zhí)行。是通過(guò)一個(gè)計(jì)數(shù)器來(lái)實(shí)現(xiàn)的,計(jì)數(shù)器的初始值是線程的數(shù)量。當(dāng)調(diào)用countDown()方法后,每當(dāng)一個(gè)線程執(zhí)行完畢后,計(jì)數(shù)器的值就-1,當(dāng)計(jì)數(shù)器的值為0時(shí),表示所有線程都執(zhí)行完畢,然后在閉鎖上等待的線程就可以恢復(fù)工作了。

          線程中調(diào)用countDown()方法開(kāi)始計(jì)數(shù);在調(diào)用await()方法的線程中,當(dāng)計(jì)數(shù)器為0后續(xù)才會(huì)繼續(xù)執(zhí)行,否則一直等待;也可以使用latch.await(timeout, unit)在等待timeout時(shí)間后如果計(jì)數(shù)器不為0,線程仍將繼續(xù)。countDown()之后的代碼不受計(jì)數(shù)器控制 與join區(qū)別,使用join的線程將被阻塞,使用countDown的線程不受影響,只有調(diào)用await的時(shí)候才會(huì)阻塞

          8. CyclicBarrier

          作用就是會(huì)讓指定數(shù)量的(數(shù)量由構(gòu)造函數(shù)指定)所有線程都等待完成后才會(huì)繼續(xù)下一步行動(dòng)。構(gòu)造函數(shù):public CyclicBarrier(int parties)

           public CyclicBarrier(int parties)?public?CyclicBarrier(int?parties,?Runnable?barrierAction)

          parties 是線程的個(gè)數(shù);barrierAction為最后一個(gè)到達(dá)線程要做的任務(wù)
          所有線程會(huì)等待全部線程到達(dá)柵欄之后才會(huì)繼續(xù)執(zhí)行,并且最后到達(dá)的線程會(huì)完成 Runnable 的任務(wù)。
          實(shí)現(xiàn)原理:在CyclicBarrier的內(nèi)部定義了一個(gè)Lock對(duì)象,每當(dāng)一個(gè)線程調(diào)用await方法時(shí),將攔截的線程數(shù)減1,然后判斷剩余攔截?cái)?shù)是否為初始值parties,如果不是,進(jìn)入Lock對(duì)象的條件隊(duì)列等待。如果是,執(zhí)行barrierAction對(duì)象的Runnable方法,然后將鎖的條件隊(duì)列中的所有線程放入鎖等待隊(duì)列中,這些線程會(huì)依次的獲取鎖、釋放鎖。

          9. Phaser

          可重復(fù)使用的同步屏障,功能類(lèi)似于CyclicBarrier和CountDownLatch,但支持更靈活的使用。

          Phaser使我們能夠建立在邏輯線程需要才去執(zhí)行下一步的障礙等。

          我們可以協(xié)調(diào)多個(gè)執(zhí)行階段,為每個(gè)程序階段重用Phaser實(shí)例。每個(gè)階段可以有不同數(shù)量的線程等待前進(jìn)到另一個(gè)階段。我們稍后會(huì)看一個(gè)使用階段的示例。

          要參與協(xié)調(diào),線程需要使用Phaser實(shí)例 register() 本身。請(qǐng)注意:這只會(huì)增加注冊(cè)方的數(shù)量,我們無(wú)法檢查當(dāng)前線程是否已注冊(cè) - 我們必須將實(shí)現(xiàn)子類(lèi)化以支持此操作。

          線程通過(guò)調(diào)用 arriAndAwaitAdvance() 來(lái)阻止它到達(dá)屏障,這是一種阻塞方法。當(dāng)數(shù)量到達(dá)等于注冊(cè)的數(shù)量時(shí),程序的執(zhí)行將繼續(xù),并且數(shù)量將增加。我們可以通過(guò)調(diào)用getPhase()方法獲取當(dāng)前數(shù)量。

          10. ReadWriteLock

          ReadWriteLock的具體實(shí)現(xiàn)是ReentrantReadWriteLock
          ReadWriteLock允許分別創(chuàng)建讀鎖跟寫(xiě)鎖

          ReadWriteLock?readWriteLock?=?new?ReentrantReadWriteLock(); Lock readLock = readWriteLock.readLock(); Lock writeLock = readWriteLock.writeLock();

          使用ReadWriteLock時(shí),適用條件是同一個(gè)數(shù)據(jù),有大量線程讀取,但僅有少數(shù)線程修改。ReadWriteLock可以保證:

          • 只允許一個(gè)線程寫(xiě)入(其他線程既不能寫(xiě)入也不能讀?。?;

          • 沒(méi)有寫(xiě)入時(shí),多個(gè)線程允許同時(shí)讀(提高性能)

          讀寫(xiě)分離鎖可以有效地幫助減少鎖競(jìng)爭(zhēng),以提高系統(tǒng)性能,讀寫(xiě)鎖讀讀之間不互斥,讀寫(xiě),寫(xiě)寫(xiě)都是互斥的

          11. Semaphore

          Semaphore 是一個(gè)計(jì)數(shù)信號(hào)量,必須由獲取它的線程釋放。常用于限制可以訪問(wèn)某些資源的線程數(shù)量,例如通過(guò) Semaphore 限流。
          對(duì)于Semaphore來(lái)說(shuō),它要保證的是資源的互斥而不是資源的同步,在同一時(shí)刻是無(wú)法保證同步的,但是卻可以保證資源的互斥。只是限制了訪問(wèn)某些資源的線程數(shù),其實(shí)并沒(méi)有實(shí)現(xiàn)同步。

          常用方法: 1、acquire(int permits)

          從此信號(hào)量獲取給定數(shù)目的許可,在提供這些許可前一直將線程阻塞,或者線程已被中斷。就好比是一個(gè)學(xué)生占兩個(gè)窗口。這同時(shí)也對(duì)應(yīng)了相應(yīng)的release方法。

          2、release(int permits)

          釋放給定數(shù)目的許可,將其返回到信號(hào)量。這個(gè)是對(duì)應(yīng)于上面的方法,一個(gè)學(xué)生占幾個(gè)窗口完事之后還要釋放多少

          3、availablePermits()

          返回此信號(hào)量中當(dāng)前可用的許可數(shù)。也就是返回當(dāng)前還有多少個(gè)窗口可用。

          4、reducePermits(int reduction)

          根據(jù)指定的縮減量減小可用許可的數(shù)目。

          5、hasQueuedThreads()

          查詢(xún)是否有線程正在等待獲取資源。

          6、getQueueLength()

          返回正在等待獲取的線程的估計(jì)數(shù)目。該值僅是估計(jì)的數(shù)字。

          7、tryAcquire(int permits, long timeout, TimeUnit unit)

          如果在給定的等待時(shí)間內(nèi)此信號(hào)量有可用的所有許可,并且當(dāng)前線程未被中斷,則從此信號(hào)量獲取給定數(shù)目的許可。

          8、acquireUninterruptibly(int permits)

          從此信號(hào)量獲取給定數(shù)目的許可,在提供這些許可前一直將線程阻塞。

          12. Exchanger

          用于兩個(gè)工作線程之間交換數(shù)據(jù)的封裝工具類(lèi),簡(jiǎn)單說(shuō)就是一個(gè)線程在完成一定的事務(wù)后想與另一個(gè)線程交換數(shù)據(jù),則第一個(gè)先拿出數(shù)據(jù)的線程會(huì)一直等待第二個(gè)線程,直到第二個(gè)線程拿著數(shù)據(jù)到來(lái)時(shí)才能彼此交換對(duì)應(yīng)數(shù)據(jù)。其定義為 Exchanger泛型類(lèi)型,其中 V 表示可交換的數(shù)據(jù)類(lèi)型,對(duì)外提供的接口很簡(jiǎn)單,具體如下:

          Exchanger():無(wú)參構(gòu)造方法。

          V exchange(V v):等待另一個(gè)線程到達(dá)此交換點(diǎn)(除非當(dāng)前線程被中斷),然后將給定的對(duì)象傳送給該線程,并接收該線程的對(duì)象。

          V exchange(V v, long timeout, TimeUnit unit):等待另一個(gè)線程到達(dá)此交換點(diǎn)(除非當(dāng)前線程被中斷或超出了指定的等待時(shí)間),然后將給定的對(duì)象傳送給該線程,并接收該線程的對(duì)象。

          13. LockSupport

          LockSupport 是一個(gè)非常方便實(shí)用的線程阻塞工具,他可以在任意位置讓線程阻塞。

          LockSupport 的靜態(tài)方法 park()可以阻塞當(dāng)前線程,類(lèi)似的還有 parkNanos(),parkUntil()等,他們實(shí)現(xiàn)了一個(gè)限時(shí)的等待。

          方法描述
          void park():阻塞當(dāng)前線程,如果調(diào)用unpark方法或者當(dāng)前線程被中斷,從能從park()方法中返回
          void park(Object blocker)功能同方法1,入?yún)⒃黾右粋€(gè)Object對(duì)象,用來(lái)記錄導(dǎo)致線程阻塞的阻塞對(duì)象,方便進(jìn)行問(wèn)題排查;
          void parkNanos(long nanos)阻塞當(dāng)前線程,最長(zhǎng)不超過(guò)nanos納秒,增加了超時(shí)返回的特性;
          void parkNanos(Object blocker, long nanos)功能同方法3,入?yún)⒃黾右粋€(gè)Object對(duì)象,用來(lái)記錄導(dǎo)致線程阻塞的阻塞對(duì)象,方便進(jìn)行問(wèn)題排查;
          void parkUntil(long deadline)阻塞當(dāng)前線程,直到deadline;
          void parkUntil(Object blocker, long deadline)功能同方法5,入?yún)⒃黾右粋€(gè)Object對(duì)象,用來(lái)記錄導(dǎo)致線程阻塞的阻塞對(duì)象,方便進(jìn)行問(wèn)題排查;

          同樣的,有阻塞的方法,當(dāng)然有喚醒的方法,什么呢?unpark(Thread) 方法。該方法可以將指定線程喚醒。

          需要注意的是:park 方法和 unpark 方法執(zhí)行順序不是那么的嚴(yán)格。比如我們?cè)?Thread 類(lèi)中提到的 suspend 方法 和resume 方法,如果順序錯(cuò)誤,將導(dǎo)致永遠(yuǎn)無(wú)法喚醒,但 park 方法和 unpark 方法則不會(huì),因?yàn)?LockSupport 使用了類(lèi)似信號(hào)量的機(jī)制。他為每一個(gè)線程準(zhǔn)備了一個(gè)許可(默認(rèn)不可用),如果許可能用,那么 park 函數(shù)會(huì)立即返回,并且消費(fèi)這個(gè)許可(也就是將許可變?yōu)椴豢捎茫?,如果許可不可用,將會(huì)阻塞。而 unpark 方法則使得一個(gè)許可變?yōu)榭捎?/p>

          14. AQS

          AQS 為 AbstractQueuedSynchronizer 的簡(jiǎn)稱(chēng)

          AQS是JDK下提供的一套用于實(shí)現(xiàn)基于FIFO等待隊(duì)列的阻塞鎖和相關(guān)的同步器的一個(gè)同步框架。這個(gè)抽象類(lèi)被設(shè)計(jì)為作為一些可用原子int值來(lái)表示狀態(tài)的同步器的基類(lèi)。AQS管理一個(gè)關(guān)于狀態(tài)信息的單一整數(shù),該整數(shù)可以表現(xiàn)任何狀態(tài)。比如 Semaphore 用它來(lái)表現(xiàn)剩余的許可數(shù), ReentrantLock 用它來(lái)表現(xiàn)擁有它的線程已經(jīng)請(qǐng)求了多少次鎖;FutureTask 用它來(lái)表現(xiàn)任務(wù)的狀態(tài)(尚未開(kāi)始、運(yùn)行、完成和取消)

          使用須知:

          Usage

          To use this class as the basis of a synchronizer, redefine thefollowing methods, as applicable, by inspecting and/or modifyingthe synchronization state using {@link #getState}, {@link#setState} and/or {@link #compareAndSetState}:{@link #tryAcquire}{@link #tryRelease}{@link #tryAcquireShared}{@link #tryReleaseShared}>{@link #isHeldExclusively}




          以上方法不需要全部實(shí)現(xiàn),根據(jù)獲取的鎖的種類(lèi)可以選擇實(shí)現(xiàn)不同的方法: 支持獨(dú)占(排他)獲取鎖的同步器應(yīng)該實(shí)現(xiàn)tryAcquire、 tryRelease、isHeldExclusively; 支持共享獲取鎖的同步器應(yīng)該實(shí)現(xiàn)tryAcquireShared、tryReleaseShared、isHeldExclusively。

          • AQS淺析

          AQS的實(shí)現(xiàn)主要在于維護(hù)一個(gè)"volatile int state"(代表共享資源)和 一個(gè)FIFO線程等待隊(duì)列(多線程爭(zhēng)用資源被阻塞時(shí)會(huì)進(jìn)入此隊(duì)列)。隊(duì)列中的每個(gè)節(jié)點(diǎn)是對(duì)線程的一個(gè)封裝,包含線程基本信息,狀態(tài),等待的資源類(lèi)型等。

          state的訪問(wèn)方式有三種:

          getState() setState() compareAndSetState()

          AQS定義兩種資源共享方式

          Exclusive(獨(dú)占,只有一個(gè)線程能執(zhí)行,如ReentrantLock) Share(共享,多個(gè)線程可同時(shí)執(zhí)行,如Semaphore/CountDownLatch) 不同的自定義同步器爭(zhēng)用共享資源的方式也不同。自定義同步器在實(shí)現(xiàn)時(shí)只需要實(shí)現(xiàn)共享資源state的獲取與釋放方式即可, 至于具體線程等待隊(duì)列的維護(hù)(如獲取資源失敗入隊(duì)/喚醒出隊(duì)等),AQS已經(jīng)在頂層實(shí)現(xiàn)好了。自定義同步器實(shí)現(xiàn)時(shí)主要實(shí)現(xiàn)以下幾種方法:

          isHeldExclusively():該線程是否正在獨(dú)占資源。只有用到condition才需要去實(shí)現(xiàn)它。

          tryAcquire(int):獨(dú)占方式。嘗試獲取資源,成功則返回true,失敗則返回false。

          tryRelease(int):獨(dú)占方式。嘗試釋放資源,成功則返回true,失敗則返回false。tryAcquireShared(int):共享方式。嘗試獲取資源。負(fù)數(shù)表示失??;0表示成功,但沒(méi)有剩余可用資源;正數(shù)表示成功,且有剩余資源。

          tryReleaseShared(int):共享方式。嘗試釋放資源,如果釋放后允許喚醒后續(xù)等待結(jié)點(diǎn)返回true,否則返回false。

          以ReentrantLock為例

          state初始化為0,表示未鎖定狀態(tài)。
          A線程lock()時(shí),會(huì)調(diào)用tryAcquire()獨(dú)占該鎖并將state+1。
          此后,其他線程再tryAcquire()時(shí)就會(huì)失敗,直到A線程unlock()到state=0(即釋放鎖)為止,其它線程才有機(jī)會(huì)獲取該鎖。
          當(dāng)然,釋放鎖之前,A線程自己是可以重復(fù)獲取此鎖的(state會(huì)累加),這就是可重入的概念。
          但要注意,獲取多少次就要釋放多么次,這樣才能保證state是能回到零態(tài)的。

          以CountDownLatch為例

          任務(wù)分為N個(gè)子線程去執(zhí)行,state也初始化為N(注意N要與線程個(gè)數(shù)一致)。
          這N個(gè)子線程是并行執(zhí)行的,每個(gè)子線程執(zhí)行完后countDown()一次,state會(huì)CAS減1。
          等到所有子線程都執(zhí)行完后(即state=0),會(huì)unpark()主調(diào)用線程,然后主調(diào)用線程就會(huì)從await()函數(shù)返回,繼續(xù)后余動(dòng)作。
          一般來(lái)說(shuō),自定義同步器要么是獨(dú)占方法,要么是共享方式,
          他們也只需實(shí)現(xiàn)tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一種即可。
          但AQS也支持自定義同步器同時(shí)實(shí)現(xiàn)獨(dú)占和共享兩種方式,如"ReentrantReadWriteLock"。

          15. 鎖基本概念

          • 公平鎖/非公平鎖

          • 可重入鎖

          • 獨(dú)享鎖/共享鎖

          • 互斥鎖/讀寫(xiě)鎖

          • 樂(lè)觀鎖/悲觀鎖

          • 分段鎖

          • 偏向鎖/輕量級(jí)鎖/重量級(jí)鎖

          • 自旋鎖

          • 公平鎖/非公平鎖

          公平鎖是指多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲取鎖。
          非公平鎖是指多個(gè)線程獲取鎖的順序并不是按照申請(qǐng)鎖的順序, 有可能后申請(qǐng)的線程比先申請(qǐng)的線程優(yōu)先獲取鎖; 有可能會(huì)造成優(yōu)先級(jí)反轉(zhuǎn)或者饑餓現(xiàn)象。
          對(duì)于Java ReentrantLock而言,通過(guò)構(gòu)造函數(shù)指定該鎖是否是公平鎖,默認(rèn)是非公平鎖。
          非公平鎖的優(yōu)點(diǎn)在于吞吐量比公平鎖大。
          對(duì)于Synchronized而言,也是一種非公平鎖。由于其并不像ReentrantLock是通過(guò)AQS的來(lái)實(shí)現(xiàn)線程調(diào)度, 所以并沒(méi)有任何辦法使其變成公平鎖。

          • 可重入鎖

          可重入鎖又名遞歸鎖,是指在同一個(gè)線程在外層方法獲取鎖的時(shí)候,在進(jìn)入內(nèi)層方法會(huì)自動(dòng)獲取鎖。
          ReentrantLock, Synchronized都是可重入鎖。
          可重入鎖的一個(gè)好處是可一定程度避免死鎖

          • 獨(dú)享(排他)鎖/共享鎖

          獨(dú)享鎖是指該鎖一次只能被一個(gè)線程所持有。
          共享鎖是指該鎖可被多個(gè)線程所持有。
          對(duì)于ReentrantLock而言,其是獨(dú)享鎖。
          但是對(duì)于Lock的另一個(gè)實(shí)現(xiàn)類(lèi)ReadWriteLock,其讀鎖是共享鎖,其寫(xiě)鎖是獨(dú)享鎖。讀鎖的共享鎖可保證并發(fā)讀是非常高效的,讀寫(xiě),寫(xiě)讀 ,寫(xiě)寫(xiě)的過(guò)程是互斥的。獨(dú)享鎖與共享鎖也是通過(guò)AQS來(lái)實(shí)現(xiàn)的,通過(guò)實(shí)現(xiàn)不同的方法,來(lái)實(shí)現(xiàn)獨(dú)享或者共享。
          對(duì)于Synchronized而言,當(dāng)然是獨(dú)享鎖。

          • 互斥鎖/讀寫(xiě)鎖

          上面講的獨(dú)享鎖/共享鎖就是一種廣義的說(shuō)法,互斥鎖/讀寫(xiě)鎖就是具體的實(shí)現(xiàn)。
          互斥鎖在Java中的具體實(shí)現(xiàn)就是ReentrantLock
          讀寫(xiě)鎖在Java中的具體實(shí)現(xiàn)就是ReadWriteLock

          • 樂(lè)觀鎖/悲觀鎖

          樂(lè)觀鎖與悲觀鎖不是指具體的什么類(lèi)型的鎖,而是指看待并發(fā)同步的角度。

          • 悲觀鎖(Synchronized 和 ReentrantLock)

          認(rèn)為對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,一定是會(huì)發(fā)生修改的,哪怕沒(méi)有修改,也會(huì)認(rèn)為修改。
          因此對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,悲觀鎖采取加鎖的形式。悲觀的認(rèn)為,不加鎖的并發(fā)操作一定會(huì)出問(wèn)題。

          • 樂(lè)觀鎖(java.util.concurrent.atomic包)

          認(rèn)為對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,是不會(huì)發(fā)生修改的。在更新數(shù)據(jù)的時(shí)候,會(huì)采用嘗試更新,不斷重新的方式更新數(shù)據(jù)。樂(lè)觀的認(rèn)為,不加鎖的并發(fā)操作是沒(méi)有事情的。
          悲觀鎖適合寫(xiě)操作非常多的場(chǎng)景,樂(lè)觀鎖適合讀操作非常多的場(chǎng)景,
          不加鎖會(huì)帶來(lái)大量的性能提升。
          悲觀鎖在Java中的使用,就是利用各種鎖。
          樂(lè)觀鎖在Java中的使用,是無(wú)鎖編程,常常采用的是CAS算法。典型的例子就是原子類(lèi),通過(guò)CAS自旋實(shí)現(xiàn)原子操作的更新。

          • 分段鎖

          分段鎖其實(shí)是一種鎖的設(shè)計(jì),并不是具體的一種鎖,ConcurrentHashMap并發(fā)的實(shí)現(xiàn)就是通過(guò)分段鎖的形式來(lái)實(shí)現(xiàn)高效的并發(fā)操作。
          ConcurrentHashMap中的分段鎖稱(chēng)為Segment, 它類(lèi)似于HashMap(JDK7與JDK8中HashMap的實(shí)現(xiàn))的結(jié)構(gòu), 即內(nèi)部擁有一個(gè)Entry數(shù)組,數(shù)組中的每個(gè)元素又是一個(gè)鏈表;同時(shí)又是一個(gè)ReentrantLock(Segment繼承了ReentrantLock)。當(dāng)需要put元素的時(shí)候,并不是對(duì)整個(gè)hashmap進(jìn)行加鎖, 而是先通過(guò)hashcode來(lái)知道他要放在那一個(gè)分段中,然后對(duì)這個(gè)分段進(jìn)行加鎖, 所以當(dāng)多線程put的時(shí)候,只要不是放在一個(gè)分段中,就實(shí)現(xiàn)了真正的并行的插入。但是,在統(tǒng)計(jì)size的時(shí)候,可就是獲取hashmap全局信息的時(shí)候,就需要獲取所有的分段鎖才能統(tǒng)計(jì)。
          分段鎖的設(shè)計(jì)目的是細(xì)化鎖的粒度,當(dāng)操作不需要更新整個(gè)數(shù)組的時(shí)候, 就僅僅針對(duì)數(shù)組中的一項(xiàng)進(jìn)行加鎖操作。

          • 偏向鎖/輕量級(jí)鎖/重量級(jí)鎖

          這三種鎖是指鎖的狀態(tài),并且是針對(duì)Synchronized。在Java 5通過(guò)引入鎖升級(jí)的機(jī)制來(lái)實(shí)現(xiàn)高效Synchronized。這三種鎖的狀態(tài)是通過(guò)對(duì)象監(jiān)視器在對(duì)象頭中的字段來(lái)表明的。

          • 偏向鎖

          是指一段同步代碼一直被一個(gè)線程所訪問(wèn),那么該線程會(huì)自動(dòng)獲取鎖。降低獲取鎖的代價(jià)。

          • 輕量級(jí)鎖

          是指當(dāng)鎖是偏向鎖的時(shí)候,被另一個(gè)線程所訪問(wèn),偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖, 其他線程會(huì)通過(guò)自旋的形式嘗試獲取鎖,不會(huì)阻塞,提高性能。

          • 重量級(jí)鎖

          是指當(dāng)鎖為輕量級(jí)鎖的時(shí)候,另一個(gè)線程雖然是自旋,但自旋不會(huì)一直持續(xù)下去, 當(dāng)自旋一定次數(shù)的時(shí)候,還沒(méi)有獲取到鎖,就會(huì)進(jìn)入阻塞,該鎖膨脹為重量級(jí)鎖。重量級(jí)鎖會(huì)讓其他申請(qǐng)的線程進(jìn)入阻塞,性能降低。

          • 自旋鎖

          在Java中,自旋鎖是指嘗試獲取鎖的線程不會(huì)立即阻塞,而是采用循環(huán)的方式去嘗試獲取鎖, 這樣的好處是減少線程上下文切換的消耗,缺點(diǎn)是循環(huán)會(huì)消耗CPU。典型的自旋鎖實(shí)現(xiàn)的例子,可以參考自旋鎖的實(shí)現(xiàn)


          --? End? --

          ———————


          1.原創(chuàng)不易,你的在看是我創(chuàng)作的動(dòng)力。

          2.歡迎關(guān)注公眾號(hào)?牧小農(nóng)的夏天「帶你一起學(xué)Java」!

          3.疫情期間,勤洗手,戴口罩,做好個(gè)人防護(hù)。


          ??????????

          ????????????????????

          “在看轉(zhuǎn)發(fā)”是最大的支持


          瀏覽 96
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美乱伦图片 | 五月丁香福利 | 做爱免费视频 | 欧美夫妻天天 | 女人一级做aa免费看 |