中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

鄭州微盟網(wǎng)站建設(shè)公司青島網(wǎng)站seo

鄭州微盟網(wǎng)站建設(shè)公司,青島網(wǎng)站seo,美業(yè)營銷策劃公司,百度百科網(wǎng)站怎么做文章目錄一、背景二、什么是JUC?三、JUC框架結(jié)構(gòu)四、JUC框架概述五、JUC中常用類匯總六、相關(guān)名詞進(jìn)程和線程進(jìn)程線程創(chuàng)建線程的幾種常見的方式并發(fā)和并行用戶線程和守護(hù)線程七、synchronized 作用范圍:八、Lock鎖(重點(diǎn))什么是 Lock鎖類型Lock接口lock()…

文章目錄

  • 一、背景
  • 二、什么是JUC?
  • 三、JUC框架結(jié)構(gòu)
  • 四、JUC框架概述
  • 五、JUC中常用類匯總
  • 六、相關(guān)名詞
    • 進(jìn)程和線程
      • 進(jìn)程
      • 線程
      • 創(chuàng)建線程的幾種常見的方式
    • 并發(fā)和并行
    • 用戶線程和守護(hù)線程
  • 七、synchronized 作用范圍:
  • 八、Lock鎖(重點(diǎn))
    • 什么是 Lock
    • 鎖類型
    • Lock接口
      • lock()、unlock()
      • newCondition
    • ReentrantLock (可重入鎖)
    • ReadWriteLock (讀寫鎖)
      • 案例
    • Lock 與的 Synchronized 區(qū)別
  • 九、Callable 接口
    • 前言:
    • 概述:
    • 實(shí)現(xiàn):
  • 十、Future 接口
    • 概述:
    • 實(shí)現(xiàn):
    • FutureTask
      • FutureTask介紹
      • FutureTask應(yīng)用場景及注意事項(xiàng)
    • 使用 Callable 和 Future🚩
  • 十一、JUC三大常用工具類
    • CountDownLatch(減計(jì)數(shù)器)
      • 概述:
      • 案例:
      • 小結(jié):
    • CyclicBarrier(加法計(jì)數(shù)器)
      • 概述:
      • 案例:
      • 小結(jié):
    • Semaphore( 信號燈)
      • 概述:
      • 案例:
      • 小結(jié):
    • 簡單講述 | Phaser & Exchanger
      • Phaser
      • Exchanger
  • 十二、總結(jié)

一、背景

如果你對多線程沒什么了解,那么從入門模塊開始。 如果你已經(jīng)入門了多線程(知道基礎(chǔ)的線程創(chuàng)建、死鎖、synchronized、lock等),那么從juc模塊開始。

新手學(xué)技術(shù)、老手學(xué)體系,高手學(xué)格局。

二、什么是JUC?

JUC實(shí)際上就是我們對于jdk中java.util .concurrent 工具包的簡稱,其結(jié)構(gòu)如下:
在這里插入圖片描述

這個(gè)包下都是Java處理線程相關(guān)的類,自jdk1.5后出現(xiàn)。目的就是為了更好的支持高并發(fā)任務(wù)。讓開發(fā)者進(jìn)行多線程編程時(shí)減少競爭條件和死鎖的問題!
在這里插入圖片描述
JUC主要是指JDK8中java.util.concurrent里提供的一系列線程并發(fā)工具,但是線程并發(fā)的問題遠(yuǎn)不止幾個(gè)工具這么簡單。要學(xué)習(xí)工具使用,更要能深入理解工具的原理以及處理線程并發(fā)問題的思路。

三、JUC框架結(jié)構(gòu)

UC是包的簡稱,JUC可能也是Java核心里最難的一塊兒,JUC指的是Java的并發(fā)工具包,里邊提供了各種各樣的控制同步和線程通信的工具類。學(xué)習(xí)JUC之前,最重要的是了解JUC的結(jié)構(gòu)是什么樣的。就如同Java的集合框架的結(jié)構(gòu)一樣,JUC也有自己框架結(jié)構(gòu),只是往往被大家忽略,筆者就簡單的梳理了下JUC的框架結(jié)構(gòu),JUC的框架結(jié)構(gòu)不同于集合,它并非是實(shí)現(xiàn)繼承框架結(jié)構(gòu)。
在這里插入圖片描述

四、JUC框架概述

JUC框架的底層在Java代碼里是Unsafe,而Unsafe是底層Jvm的實(shí)現(xiàn)。有了Unsafe的支持實(shí)現(xiàn)了一些了支持原子型操作的Atomic類,然后上層才有了我們熟知的AQS,和LockSupport等類。有了這些之后才是各種讀寫鎖各種線程通信以及同步工具的實(shí)現(xiàn)類。

五、JUC中常用類匯總

  • JUC的atomic包下運(yùn)用了CAS的AtomicBoolean、AtomicInteger、AtomicReference等原子變量類

  • JUC的locks包下的AbstractQueuedSynchronizer(AQS)以及使用AQS的ReentantLock(顯式鎖)、ReentrantReadWriteLock

  • 附:運(yùn)用了AQS的類還有:
    Semaphore、CountDownLatch、ReentantLock(顯式鎖)、ReentrantReadWriteLock

  • JUC下的一些同步工具類:
    CountDownLatch(閉鎖)、Semaphore(信號量)、CyclicBarrier(柵欄)、FutureTask

  • JUC下的一些并發(fā)容器類:
    ConcurrentHashMap、CopyOnWriteArrayList

  • 讀寫分離:
    CopyOnWriteArrayList
    寫操作在一個(gè)復(fù)制的數(shù)組上進(jìn)行,讀操作還是在原始數(shù)組中進(jìn)行,讀寫分離,互不影響。
    寫操作需要加鎖,防止并發(fā)寫入時(shí)導(dǎo)致寫入數(shù)據(jù)丟失。
    寫操作結(jié)束之后需要把原始數(shù)組指向新的復(fù)制數(shù)組。

  • JUC下的一些Executor框架的相關(guān)類:
    線程池的工廠類->Executors 線程池的實(shí)現(xiàn)類->ThreadPoolExecutor/ForkJoinPool

  • JUC下的一些阻塞隊(duì)列實(shí)現(xiàn)類:
    ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue

在這里插入圖片描述

  1. tools(工具類):又叫信號量三組工具類,包含有
  • CountDownLatch(閉鎖) 是一個(gè)同步輔助類,在完成一組正在其他線程中執(zhí)行的操作之前,它允許一個(gè)或多個(gè)線程一直等待

  • CyclicBarrier(柵欄) 之所以叫barrier,是因?yàn)槭且粋€(gè)同步輔助類,允許一組線程互相等待,直到到達(dá)某個(gè)公共屏障點(diǎn) ,并且在釋放等待線程后可以重用。

  • Semaphore(信號量) 是一個(gè)計(jì)數(shù)信號量,它的本質(zhì)是一個(gè)“共享鎖“。信號量維護(hù)了一個(gè)信號量許可集。線程可以通過調(diào)用 acquire()來獲取信號量的許可;當(dāng)信號量中有可用的許可時(shí),線程能獲取該許可;否則線程必須等待,直到有可用的許可為止。 線程可以通過release()來釋放它所持有的信號量許可。

  1. executor(執(zhí)行者):是Java里面線程池的頂級接口,但它只是一個(gè)執(zhí)行線程的工具,真正的線程池接口是ExecutorService,里面包含的類有:
  • ScheduledExecutorService 解決那些需要任務(wù)重復(fù)執(zhí)行的問題
  • ScheduledThreadPoolExecutor 周期性任務(wù)調(diào)度的類實(shí)現(xiàn)
  1. atomic(原子性包):是JDK提供的一組原子操作類,包含有AtomicBoolean、AtomicInteger、AtomicIntegerArray等原子變量類,他們的實(shí)現(xiàn)原理大多是持有它們各自的對應(yīng)的類型變量value,而且被volatile關(guān)鍵字修飾了。這樣來保證每次一個(gè)線程要使用它都會(huì)拿到最新的值。

  2. locks(鎖包):是JDK提供的鎖機(jī)制,相比synchronized關(guān)鍵字來進(jìn)行同步鎖,功能更加強(qiáng)大,它為鎖提供了一個(gè)框架,該框架允許更靈活地使用鎖包含的實(shí)現(xiàn)類有:

  • ReentrantLock 它是獨(dú)占鎖,是指只能被獨(dú)自占領(lǐng),即同一個(gè)時(shí)間點(diǎn)只能被一個(gè)線程鎖獲取到的鎖。

  • ReentrantReadWriteLock 它包括子類ReadLock和WriteLock。ReadLock是共享鎖,而WriteLock是獨(dú)占鎖。

  • LockSupport 它具備阻塞線程和解除阻塞線程的功能,并且不會(huì)引發(fā)死鎖。

  1. collections(集合類):主要是提供線程安全的集合, 比如:
  • ArrayList對應(yīng)的高并發(fā)類是CopyOnWriteArrayList,

  • HashSet對應(yīng)的高并發(fā)類是 CopyOnWriteArraySet,

  • HashMap對應(yīng)的高并發(fā)類是ConcurrentHashMap等等

六、相關(guān)名詞

進(jìn)程和線程

進(jìn)程

概述
進(jìn)程(Process) 是計(jì)算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ)。 在當(dāng)代面向線程設(shè)計(jì)的計(jì)算機(jī)結(jié)構(gòu)中,進(jìn)程是線程的容器。程序是指令、數(shù)據(jù)及其組織形式的描述,進(jìn)程是程序的實(shí)體

定義

狹義定義:進(jìn)程是正在運(yùn)行的程序的實(shí)例(an instance of a computer program that is being executed)。

廣義定義:進(jìn)程是一個(gè)具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合的一次運(yùn)行活動(dòng)。它是操作系統(tǒng)動(dòng)態(tài)執(zhí)行的基本單元,在傳統(tǒng)的操作系統(tǒng)中,進(jìn)程既是基本的分配單元,也是基本的執(zhí)行單元。

線程

線程(thread) 是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位。它被包含在進(jìn)程之 中,是進(jìn)程中的實(shí)際運(yùn)作單位。一條線程指的是進(jìn)程中一個(gè)單一順序的控制流, 一個(gè)進(jìn)程中可以并發(fā)多個(gè)線程,每條線程并行執(zhí)行不同的任務(wù)。

  1. 線程是獨(dú)立調(diào)度和分派的基本單位。
  2. 同一進(jìn)程中的多條線程將共享該進(jìn)程中的全部系統(tǒng)資源。
  3. 一個(gè)進(jìn)程可以有很多線程,每條線程并行執(zhí)行不同的任務(wù)。可并發(fā)執(zhí)行。

當(dāng)然,我們Java都知道的線程的同步是Java多線程編程的難點(diǎn),因?yàn)橐o哪些地方是共享資源(競爭資源),什么時(shí)候要考慮同步等,都是編程中的難點(diǎn)。

創(chuàng)建線程的幾種常見的方式

  1. 通過實(shí)現(xiàn)Runnable接口來創(chuàng)建Thread線程
  2. 通過繼承Thread類來創(chuàng)建一個(gè)線程
  3. 通過實(shí)現(xiàn)Callable接口來創(chuàng)建Thread線程

備注:詳細(xì)看我寫的上一篇文章:Java實(shí)現(xiàn)多線程的4種方式

并發(fā)和并行

在了解并發(fā)和并行之前,讓我們先來看一看串行是什么樣的吧。

  1. 串行模式:
  • 串行模式:即表示所有任務(wù)都是按先后順序進(jìn)行。串行是一次只能取的一個(gè)任務(wù),并執(zhí)行這個(gè)任務(wù)。
  • 舉個(gè)生活中的小例子:就是在火車站買票,今天只開放這一個(gè)窗口賣票,那么我們只有等到前面的人都買了,才能輪到我們?nèi)ベI。即按先后順序買到票。
  1. 并行模式:
  • 概述:一組程序按獨(dú)立異步的速度執(zhí)行,無論從微觀還是宏觀,程序都是一起執(zhí)行的。對比地,并發(fā)是指:在同一個(gè)時(shí)間段內(nèi),兩個(gè)或多個(gè)程序執(zhí)行,有時(shí)間上的重疊(宏觀上是同時(shí),微觀上仍是順序執(zhí)行)。
  • 并行模式:并行意味著可以同時(shí)取得多個(gè)任務(wù),并同時(shí)去執(zhí)行所取得的這些任務(wù)。
    我們還是用上面那個(gè)例子:還是在買票,以前是只有一個(gè)窗口賣票,但是近幾年發(fā)展起來了,現(xiàn)在有五個(gè)窗口賣票啦,大大縮短了人們買票的時(shí)間。
  • 并行模式相當(dāng)于將長長的一條隊(duì)列,劃分成了多條短隊(duì)列,所以并行縮短了任務(wù)隊(duì)列的長度。不過并行的效率,一方面受多進(jìn)程/線程編碼的好壞的影響,另一方面也受硬件角度上的CPU的影響。
  1. 并發(fā):
  • 并發(fā):并發(fā)指的是多個(gè)程序可以同時(shí)運(yùn)行的一種現(xiàn)象,并發(fā)的重點(diǎn)在于它是一種現(xiàn)象,并發(fā)描述的是多進(jìn)程同時(shí)運(yùn)行的現(xiàn)象。但真正意義上,一個(gè)單核心CPU任一時(shí)刻都只能運(yùn)行一個(gè)線程。所以此處的"同時(shí)運(yùn)行"表示的不是真的同一時(shí)刻有多個(gè)線程運(yùn)行的現(xiàn)象(這是并行的概念),而是提供了一種功能讓用戶看來多個(gè)程序同時(shí)運(yùn)行起來了,但實(shí)際上這些程序中的進(jìn)程不是一直霸占 CPU 的,而是根據(jù)CPU的調(diào)度,執(zhí)行一會(huì)兒停一會(huì)兒。
  1. 小小的總結(jié)一下:
  • 并發(fā):即同一時(shí)刻多個(gè)線程在訪問同一個(gè)資源,多個(gè)線程對一個(gè)點(diǎn)
    例子:秒殺活動(dòng)、12306搶回家的票啦、搶演唱會(huì)的票…

  • 并行:多個(gè)任務(wù)一起執(zhí)行,之后再匯總
    例子:電飯煲煮飯、用鍋炒菜,兩個(gè)事情一起進(jìn)行,(最后我們一起干飯啦干飯啦😁)

用戶線程和守護(hù)線程

  1. 用戶線程:指不需要內(nèi)核支持而在用戶程序中實(shí)現(xiàn)的線程,其不依賴于操作系統(tǒng)核心,應(yīng)用進(jìn)程利用線程庫提供創(chuàng)建、同步、調(diào)度和管理線程的函數(shù)來控制用戶線程。

  2. 守護(hù)線程:是指在程序運(yùn)行的時(shí)候在后臺提供一種通用服務(wù)的線程,用來服務(wù)于用戶線程;不需要上層邏輯介入,當(dāng)然我們也可以手動(dòng)創(chuàng)建一個(gè)守護(hù)線程。(用白話來說:就是守護(hù)著用戶線程,當(dāng)用戶線程死亡,守護(hù)線程也會(huì)隨之死亡)

比如垃圾回收線程就是一個(gè)很稱職的守護(hù)者,并且這種線程并不屬于程序中不可或缺的部分。因此,當(dāng)所有的非守護(hù)線程結(jié)束時(shí),程序也就終止了,同時(shí)會(huì)殺死進(jìn)程中的所有守護(hù)線程。反過來說,只要任何非守護(hù)線程還在運(yùn)行,程序就不會(huì)終止。
用一個(gè)簡單代碼來模擬一下:
未設(shè)置為守護(hù)線程時(shí):主線程執(zhí)行完成了,但是我們自己創(chuàng)建的線程仍然未結(jié)束。
在這里插入圖片描述

設(shè)置為守護(hù)線程后:明顯可以看到,當(dāng)主線程執(zhí)行完成后,我們設(shè)置為守護(hù)線程的那個(gè)線程也被強(qiáng)制結(jié)束了。
在這里插入圖片描述

setDaemon就是設(shè)置為是否為守護(hù)線程。

七、synchronized 作用范圍:

synchronized 是 Java 中的關(guān)鍵字,是一種同步鎖。它修飾的對象有以下幾種:

  1. 修飾某一處代碼塊,被修飾的代碼塊稱為同步語句塊。作用范圍就是{}之間。作用的對象是調(diào)用這個(gè)代碼塊的對象。
synchronized (this){System.out.println("同步代碼塊 ");
}
  1. 修飾在方法上,被修飾的方法就稱為同步方法。作用范圍則是整個(gè)方法。作用的對象則是調(diào)用這個(gè)方法的對象。
public synchronized void sale() {}

注:synchronized 關(guān)鍵字不能被繼承,如果父類中某方法使用了synchronized 關(guān)鍵字,字類又正巧覆蓋了,此時(shí),字類默認(rèn)情況下是不同步的,必須顯示的在子類的方法上加上才可。當(dāng)然,如果在字類中調(diào)用父類中的同步方法,這樣雖然字類并沒有同步方法,但子類調(diào)用父類的同步方法,子類方法也相當(dāng)同步了。

  1. 修飾某個(gè)靜態(tài)的方法,其作用的范圍是整個(gè)靜態(tài)方法,作用的對象是這個(gè)類的所有對象。
public static synchronized void test(){}
  1. 修飾某個(gè)類,其作用的范圍是 synchronized 后面括號括起來的部分,作用的對象是這個(gè)類的所有對象。
class Ticket {public void sale() {synchronized (Ticket.class) {}}
}

八、Lock鎖(重點(diǎn))

什么是 Lock

Lock 鎖實(shí)現(xiàn)提供了比使用同步方法和語句可以獲得的更廣泛的鎖操作。

鎖類型

可重入鎖:在執(zhí)行對象中所有同步方法不用再次獲得鎖
可中斷鎖:在等待獲取鎖過程中可中斷
公平鎖: 按等待獲取鎖的線程的等待時(shí)間進(jìn)行獲取,等待時(shí)間長的具有優(yōu)先獲取鎖權(quán)利
讀寫鎖:對資源讀取和寫入的時(shí)候拆分為2部分處理,讀的時(shí)候可以多線程一起讀,寫的時(shí)候必須同步地寫

Lock接口

public interface Lock {void lock(); //獲得鎖。/**除非當(dāng)前線程被中斷,否則獲取鎖。如果可用,則獲取鎖并立即返回。如果鎖不可用,則當(dāng)前線程將出于線程調(diào)度目的而被禁用并處于休眠狀態(tài),直到發(fā)生以下兩種情況之一:鎖被當(dāng)前線程獲取; 要么其他一些線程中斷當(dāng)前線程,支持中斷獲取鎖。如果當(dāng)前線程:在進(jìn)入此方法時(shí)設(shè)置其中斷狀態(tài); 要么獲取鎖時(shí)中斷,支持中斷獲取鎖,*/void lockInterruptibly() throws InterruptedException; /**僅在調(diào)用時(shí)空閑時(shí)才獲取鎖。如果可用,則獲取鎖并立即返回值為true 。 如果鎖不可用,則此方法將立即返回false值。*/boolean tryLock();//比上面多一個(gè)等待時(shí)間 boolean tryLock(long time, TimeUnit unit) throws InterruptedException;  // 解鎖void unlock(); //返回綁定到此Lock實(shí)例的新Condition實(shí)例。Condition newCondition();  。
}

下面講幾個(gè)常用方法的使用。

lock()、unlock()

lock()是最常用的方法之一,作用就是獲取鎖,如果鎖已經(jīng)被其他線程獲得,則當(dāng)前線程將被禁用以進(jìn)行線程調(diào)度,并處于休眠狀態(tài),等待,直到獲取鎖。

如果使用到了lock的話,那么必須去主動(dòng)釋放鎖,就算發(fā)生了異常,也需要我們主動(dòng)釋放鎖,因?yàn)閘ock并不會(huì)像synchronized一樣被自動(dòng)釋放。所以使用lock的話,必須是在try{}catch(){}中進(jìn)行,并將釋放鎖的代碼放在finally{}中,以確保鎖一定會(huì)被釋放,以防止死鎖現(xiàn)象的發(fā)生。

unlock()的作用就是主動(dòng)釋放鎖。
lock接口的類型有好幾個(gè)實(shí)現(xiàn)類,這里是隨便找了個(gè)。

Lock lock = new ReentrantLock();
try {lock.lock();System.out.println("上鎖了");
}catch (Exception e){e.printStackTrace();
}finally {lock.unlock();System.out.println("解鎖了");
}

newCondition

關(guān)鍵字 synchronized 與 wait()/notify()這兩個(gè)方法一起使用可以實(shí)現(xiàn)等待/通知模式, Lock 鎖的 newContition()方法返回 Condition 對象,Condition 類 也可以實(shí)現(xiàn)等待/通知模式。 用 notify()通知時(shí),JVM 會(huì)隨機(jī)喚醒某個(gè)等待的線程, 使用 Condition 類可以 進(jìn)行選擇性通知, Condition 比較常用的兩個(gè)方法:

  • await():會(huì)使當(dāng)前線程等待,同時(shí)會(huì)釋放鎖,當(dāng)?shù)鹊狡渌€程調(diào)用signal()方法時(shí),此時(shí)這個(gè)沉睡線程會(huì)重新獲得鎖并繼續(xù)執(zhí)行代碼(在哪里沉睡就在哪里喚醒)。
  • signal():用于喚醒一個(gè)等待的線程。

注意:在調(diào)用 Condition 的 await()/signal()方法前,也需要線程持有相關(guān) 的 Lock 鎖,調(diào)用 await()后線程會(huì)釋放這個(gè)鎖,在調(diào)用singal()方法后會(huì)從當(dāng)前 Condition對象的等待隊(duì)列中,喚醒一個(gè)線程,后被喚醒的線程開始嘗試去獲得鎖, 一旦成功獲得鎖就繼續(xù)往下執(zhí)行。

在這個(gè)地方我們舉個(gè)例子來用代碼寫一下:

這里就不舉例synchronized 實(shí)現(xiàn)了,道理都差不多。

例子:我們有兩個(gè)線程,實(shí)現(xiàn)對一個(gè)初始值是0的number變量,一個(gè)線程當(dāng)number = =0時(shí) 對number值+1,另外一個(gè)線程當(dāng)number = = 1時(shí)對number-1。

class Share {private Integer number = 0;private ReentrantLock lock = new ReentrantLock();private Condition newCondition = lock.newCondition();// +1 的方法public void incr() {try {lock.lock(); // 加鎖while (number != 0) {newCondition.await();//沉睡}number++;System.out.println(Thread.currentThread().getName() + "::" + number);newCondition.signal(); //喚醒另一個(gè)沉睡的線程 } catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}// -1 的方法public void decr() {try {lock.lock();while (number != 1) {newCondition.await();}number--;System.out.println(Thread.currentThread().getName() + "::" + number);newCondition.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}
}public class LockDemo2 {public static void main(String[] args) {Share share = new Share();new Thread(()->{for (int i=0;i<=10;i++){share.incr();}},"AA").start();new Thread(()->{for (int i=0;i<=10;i++){share.decr();}},"BB").start();/*** AA::1* BB::0* AA::1* BB::0* .....*/     }
}

ReentrantLock (可重入鎖)

ReentrantLock,意思是“可重入鎖”。ReentrantLock 是唯一實(shí)現(xiàn)了 Lock 接口的類,并且 ReentrantLock 提供了更 多的方法。

可重入鎖:什么是 “可重入”,可重入就是說某個(gè)線程已經(jīng)獲得某個(gè)鎖,可以再次獲取鎖而不會(huì)出現(xiàn)死鎖

package com.crush.juc02;import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockDemo {public static void main(String[] args) {ReentrantLock lock = new ReentrantLock();new Thread(new Runnable() {@Overridepublic void run() {try {lock.lock();System.out.println("第1次獲取鎖,這個(gè)鎖是:" + lock);for (int i = 2;i<=11;i++){try {lock.lock();System.out.println("第" + i + "次獲取鎖,這個(gè)鎖是:" + lock);try {Thread.sleep(new Random().nextInt(200));} catch (InterruptedException e) {e.printStackTrace();}} finally {lock.unlock();// 如果把這里注釋掉的話,那么程序就會(huì)陷入死鎖當(dāng)中。}}} finally {lock.unlock();}}}).start();new Thread(new Runnable() {@Overridepublic void run() {try {lock.lock();System.out.println("這里是為了測試死鎖而多寫一個(gè)的線程");} finally {lock.unlock();}}}).start();}
}
/*** 第1次獲取鎖,這個(gè)鎖是:java.util.concurrent.locks.ReentrantLock@6b5fde1f[Locked by thread Thread-0]* 第2次獲取鎖,這個(gè)鎖是:java.util.concurrent.locks.ReentrantLock@6b5fde1f[Locked by thread Thread-0]* 第3次獲取鎖,這個(gè)鎖是:java.util.concurrent.locks.ReentrantLock@6b5fde1f[Locked by thread Thread-0]* ...*/

死鎖的話,程序就無法停止,直到資源耗盡或主動(dòng)終止。
在這里插入圖片描述

代碼中也稍微提了一下死鎖的概念,在使用Lock中必須手動(dòng)解鎖,不然就會(huì)可能造成死鎖的現(xiàn)象。

ReadWriteLock (讀寫鎖)

ReadWriteLock 也是一個(gè)接口,在它里面只定義了兩個(gè)方法:

public interface ReadWriteLock {// 獲取讀鎖Lock readLock();// 獲取寫鎖Lock writeLock();
}

分為一個(gè)讀鎖一個(gè)寫鎖,將讀寫進(jìn)行了分離,使可以多個(gè)線程進(jìn)行讀操作,從而提高了效率。
ReentrantReadWriteLock 實(shí)現(xiàn)了 ReadWriteLock 接口。里面提供了更豐富的方法,當(dāng)然最主要的還是獲取寫鎖(writeLock)和讀鎖(readLock)。

案例

假如多個(gè)線程要進(jìn)行讀的操作,我們用Synchronized 來實(shí)現(xiàn)的話。

public class SynchronizedDemo2 {public static void main(String[] args) {final SynchronizedDemo2 test = new SynchronizedDemo2();new Thread(()->{test.get(Thread.currentThread());}).start();new Thread(()->{test.get(Thread.currentThread());}).start();}public synchronized void get(Thread thread) {long start = System.currentTimeMillis();while(System.currentTimeMillis() - start <= 1) {System.out.println(thread.getName()+"正在進(jìn)行讀操作");}System.out.println(thread.getName()+"讀操作完畢");}
}
/*** 輸出* Thread-0正在進(jìn)行讀操作* Thread-0讀操作完畢* Thread-1正在進(jìn)行讀操作* Thread-1正在進(jìn)行讀操作* Thread-1正在進(jìn)行讀操作* ....* Thread-1讀操作完畢*/

改成讀寫鎖之后

public class SynchronizedDemo2 {private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();public static void main(String[] args) {final SynchronizedDemo2 test = new SynchronizedDemo2();new Thread(()->{test.get2(Thread.currentThread());}).start();new Thread(()->{test.get2(Thread.currentThread());}).start();}public void get2(Thread thread) {rwl.readLock().lock();try {long start = System.currentTimeMillis();while(System.currentTimeMillis() - start <= 1) {System.out.println(thread.getName()+"正在進(jìn)行讀操作");}System.out.println(thread.getName()+"讀操作完畢");} finally {rwl.readLock().unlock();}}
}
/*** 輸出* Thread-0正在進(jìn)行讀操作* Thread-0讀操作完畢* Thread-1正在進(jìn)行讀操作* Thread-1讀操作完畢*/

結(jié)論:改用讀寫鎖后 線程1和線程2 同時(shí)在讀,可以感受到效率的明顯提升。

注意:

  • 若此時(shí)已經(jīng)有一個(gè)線程占用了讀鎖,此時(shí)其他線程申請讀鎖是可以的,但是若此時(shí)其他線程申請寫鎖,則只有等待讀鎖釋放,才能成功獲得。
  • 若此時(shí)已經(jīng)有一個(gè)線程占用了寫鎖,那么此時(shí)其他線程申請寫鎖或讀鎖,都只有持有寫鎖的線程釋放寫鎖,才能成功獲得。

Lock 與的 Synchronized 區(qū)別

類別synchronizedLock
存在層次Java的關(guān)鍵字,在jvm層面上是一個(gè)接口
鎖的獲取假設(shè)A線程獲得鎖,B線程等待。如果A線程阻塞,B線程會(huì)一直等待分情況而定,Lock有多個(gè)鎖獲取的方式,具體下面會(huì)說道,大致就是可以嘗試獲得鎖,線程可以不用一直等待
鎖的釋放1、當(dāng) synchronized 方法或者 synchronized 代碼塊執(zhí)行完之后, 系統(tǒng)會(huì)自動(dòng)讓線程釋放對鎖的占用 (不需要手動(dòng)釋放鎖)2、若線程執(zhí)行發(fā)生異常,jvm會(huì)讓線程釋放鎖在finally中必須釋放鎖,不然容易造成線程死鎖現(xiàn)象 (需要手動(dòng)釋放鎖)
鎖狀態(tài)無法判斷可以判斷
鎖類型鎖類型可重入 可判斷 可公平(兩者皆可)
性能前提:大量線程情況下 同步效率較低前提:大量線程情況下 同步效率比synchronized高的多

Lock可以提高多個(gè)線程進(jìn)行讀操作的效率。

九、Callable 接口

前言:

在上上篇文章中,創(chuàng)建線程那個(gè)小角落,提到了這個(gè),但是當(dāng)時(shí)只是匆匆忙忙講了一下。到這里再全面性的講解一下。
我們以前使用實(shí)現(xiàn)Runnable接口的方式來創(chuàng)建線程,但是Runnable的run() 存在一個(gè)缺陷問題,就是不能將執(zhí)行完的結(jié)果返回。
Java就是為了能夠?qū)崿F(xiàn)這個(gè)功能,在jdk1.5中提出了Callable接口。

概述:

Callable 接口位于java.util.concurrent包下。

@FunctionalInterface
public interface Callable<V> {V call() throws Exception; //計(jì)算結(jié)果,如果無法計(jì)算則拋出異常。
}

Callable 類似于Runnable 接口,但Runnable 接口中的run()方法不會(huì)返回結(jié)果,并且也無法拋出經(jīng)過檢查的異常,但是Callable中call()方法能夠返回計(jì)算結(jié)果,并且也能夠拋出經(jīng)過檢查的異常。

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

通過實(shí)現(xiàn)Callable接口創(chuàng)建線程詳細(xì)步驟:Runnable直接看代碼就知道了哈。

  1. 創(chuàng)建實(shí)現(xiàn)Callable接口的類SomeCallable

  2. 創(chuàng)建一個(gè)類對象:Callable oneCallable = new SomeCallable();

  3. 由Callable創(chuàng)建一個(gè)FutureTask對象:FutureTask futureTask= new FutureTask(oneCallable);
    注釋:FutureTask是一個(gè)包裝器,它通過接受Callable來創(chuàng)建,它同時(shí)實(shí)現(xiàn)了Future和Runnable接口。

  4. 由FutureTask創(chuàng)建一個(gè)Thread對象:Thread oneThread = new Thread(futureTask);

  5. 啟動(dòng)線程:oneThread.start();

public class Demo1 {public static void main(String[] args) {new Thread(new RunnableDemo1(),"AA").start();FutureTask<Integer> futureTask = new FutureTask<>(new CallableDemo<Integer>());new Thread(futureTask,"BB").start();// 在線程執(zhí)行完后,我們可以通過futureTask的get方法來獲取到返回的值。System.out.println(futureTask.get());}
}
class RunnableDemo1 implements  Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"::通過實(shí)現(xiàn)Runnable來執(zhí)行任務(wù)");}
}
class CallableDemo<Integer> implements Callable<java.lang.Integer> {@Overridepublic java.lang.Integer call() throws Exception {System.out.println(Thread.currentThread().getName()+"::通過實(shí)現(xiàn)Callable接口來執(zhí)行任務(wù),并返回結(jié)果!");return 1024;}
}
/*** AA::通過實(shí)現(xiàn)Runnable來執(zhí)行任務(wù)* BB::通過實(shí)現(xiàn)Callable接口來執(zhí)行任務(wù),并返回結(jié)果!* 1024*/

這里之所以要轉(zhuǎn)成 FutureTask 放進(jìn) Thread中去,是因?yàn)镃allable 本身與Thread沒有關(guān)系,通過FutureTask 才能和Thread產(chǎn)生聯(lián)系。

十、Future 接口

概述:

Future 接口同樣位于java.util.concurrent包下。
Future接口提供方法來檢測任務(wù)是否被執(zhí)行完,等待任務(wù)執(zhí)行完獲得結(jié)果,也可以設(shè)置任務(wù)執(zhí)行的超時(shí)時(shí)間。這個(gè)設(shè)置超時(shí)的方法就是實(shí)現(xiàn)Java程序執(zhí)行超時(shí)的關(guān)鍵。

public interface Future<V> {boolean cancel(boolean mayInterruptIfRunning); //嘗試取消此任務(wù)的執(zhí)行。boolean isCancelled();//如果此任務(wù)在正常完成之前被取消,則返回true boolean isDone(); //如果此任務(wù)完成,則返回true 。 完成可能是由于正常終止、異常或取消——在所有這些情況下,此方法將返回true V get() throws InterruptedException, ExecutionException; //獲得任務(wù)計(jì)算結(jié)果V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;//可等待多少時(shí)間去獲得任務(wù)計(jì)算結(jié)果
}

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

Future模式通俗點(diǎn)來描述就是:我有一個(gè)任務(wù),提交給了Future,Future替我完成這個(gè)任務(wù)。期間我自己可以去做任何想做的事情。一段時(shí)間之后,我就便可以從Future那兒取出結(jié)果。

public class Demo1 {public static void main(String[] args) throws ExecutionException, InterruptedException {FutureTask<Integer> futureTask = new FutureTask<>(new CallableDemo<Integer>());new Thread(futureTask,"BB").start();System.out.println(futureTask.get());// 我們來測試一下任務(wù)是否已經(jīng)完成System.out.println(futureTask.isDone());}
}
class CallableDemo<Integer> implements Callable<java.lang.Integer> {@Overridepublic java.lang.Integer call() throws Exception {System.out.println(Thread.currentThread().getName()+"::通過實(shí)現(xiàn)Callable接口來執(zhí)行任務(wù),并返回結(jié)果!");return 1024;}
}

Future 用于存儲從另一個(gè)線程獲得的結(jié)果。如果只是簡單創(chuàng)建線程,直接使用Runnable就可以,想要獲得任務(wù)返回值,就用Future。

FutureTask

FutureTask介紹

位于 java.util.concurrent包下。
可取消的異步計(jì)算。 此類提供Future的基本實(shí)現(xiàn),具有啟動(dòng)和取消計(jì)算、查詢以查看計(jì)算是否完成以及檢索計(jì)算結(jié)果的方法。 計(jì)算完成后才能檢索結(jié)果; 如果計(jì)算尚未完成, get方法將阻塞。 一旦計(jì)算完成,就不能重新開始或取消計(jì)算(除非使用runAndReset調(diào)用計(jì)算)。結(jié)構(gòu)圖:
在這里插入圖片描述

FutureTask實(shí)現(xiàn)了 Runnable 和 Future接口,并方便地將兩種功能組合在一起。并且通過構(gòu)造函數(shù)提供Callable來創(chuàng)建FutureTask,就可以提供給Thread來創(chuàng)建線程啦。
FutureTask有以下三種狀態(tài):

  • 未啟動(dòng)狀態(tài):還未執(zhí)行run()方法。
  • 已啟動(dòng)狀態(tài):已經(jīng)在執(zhí)行run()方法。
  • 完成狀態(tài):已經(jīng)執(zhí)行完run()方法,或者被取消了,亦或者方法中發(fā)生異常而導(dǎo)致中斷結(jié)束。

FutureTask應(yīng)用場景及注意事項(xiàng)

應(yīng)用場景:

  • 在主線程執(zhí)行那種比較耗時(shí)的操作時(shí),但同時(shí)又不能去阻塞主線程時(shí),就可以將這樣的任務(wù)交給FutureTask對象在后臺完成,然后等之后主線程需要的時(shí)候,就可以直接get()來獲得返回?cái)?shù)據(jù)或者通過isDone()來獲得任務(wù)的狀態(tài)。
  • 一般FutureTask多應(yīng)用于耗時(shí)的計(jì)算,這樣主線程就可以把一個(gè)耗時(shí)的任務(wù)交給FutureTask,然后等到完成自己的任務(wù)后,再去獲取計(jì)算結(jié)果

注意:

  • 僅在計(jì)算完成時(shí)才能檢索結(jié)果;如果計(jì)算尚未完成,則阻塞 get 方法。
  • 一旦計(jì) 算完成,就不能再重新開始或取消計(jì)算。
  • get 方法而獲取結(jié)果只有在計(jì)算完成時(shí)獲取,否則會(huì)一直阻塞直到任務(wù)轉(zhuǎn)入完成狀態(tài),然后會(huì)返回結(jié)果或者拋出異常。
  • 因?yàn)橹粫?huì)計(jì)算一次,因此通常get方法放到最后。

使用放在下一小節(jié)啦👇

使用 Callable 和 Future🚩

這里的使用其實(shí)在上文已經(jīng)提到過了,這里就將其更完善一些吧。

public class CallableDemo2 {public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {CallableAndFutureTest callableAndFutureTest = new CallableAndFutureTest();FutureTask<String> task = new FutureTask<>(callableAndFutureTest);new Thread(task).start();//        System.out.println("嘗試取消任務(wù),傳true表示取消任務(wù),false則不取消任務(wù)::"+task.cancel(true));System.out.println("判斷任務(wù)是否已經(jīng)完成::"+task.isDone());//結(jié)果已經(jīng)計(jì)算出來,則立馬取出來,如若摸沒有計(jì)算出來,則一直等待,直到結(jié)果出來,或任務(wù)取消或發(fā)生異常。System.out.println("阻塞式獲取結(jié)果::"+task.get());System.out.println("在獲取結(jié)果時(shí),給定一個(gè)等待時(shí)間,如果超過等待時(shí)間還未獲取到結(jié)果,則會(huì)主動(dòng)拋出超時(shí)異常::"+task.get(2, TimeUnit.SECONDS));}
}class CallableAndFutureTest implements Callable<String> {@Overridepublic String call() throws Exception {String str="";for (int i=0;i<10;i++){str+=String.valueOf(i);Thread.sleep(100);}return str;}
}

十一、JUC三大常用工具類

CountDownLatch(減計(jì)數(shù)器)

概述:

CountDownLatch位于 java.util.concurrent包下。
CountDownLatch是一個(gè)同步輔助類,允許一個(gè)或多個(gè)線程等待,一直到其他線程執(zhí)行的操作完成后再執(zhí)行。
CountDownLatch是通過一個(gè)計(jì)數(shù)器來實(shí)現(xiàn)的,計(jì)數(shù)器的初始值是線程的數(shù)量。每當(dāng)有一個(gè)線程執(zhí)行完畢后,然后通過 countDown 方法來讓計(jì)數(shù)器的值-1,當(dāng)計(jì)數(shù)器的值為0時(shí),表示所有線程都執(zhí)行完畢,然后繼續(xù)執(zhí)行 await 方法 之后的語句,即在鎖上等待的線程就可以恢復(fù)工作了。
CountDownLatch中主要有兩個(gè)方法:

  1. countDown:
  • 遞減鎖存器的計(jì)數(shù),如果計(jì)數(shù)達(dá)到零,則釋放所有等待的線程。
  • 如果當(dāng)前計(jì)數(shù)大于零,則遞減。 如果新計(jì)數(shù)為零,則為線程調(diào)度目的重新啟用所有等待線程。
  • 如果當(dāng)前計(jì)數(shù)為零,則什么也不會(huì)發(fā)生。
public void countDown() {sync.releaseShared(1);
}
  1. await:
  • 使當(dāng)前線程等待直到閂鎖倒計(jì)時(shí)為零,除非線程被中斷。
  • 如果當(dāng)前計(jì)數(shù)為零,則此方法立即返回。即await 方法阻塞的線程會(huì)被喚醒,繼續(xù)執(zhí)行
  • 如果當(dāng)前計(jì)數(shù)大于零,則當(dāng)前線程出于線程調(diào)度目的而被禁用并處于休眠狀態(tài)
public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}

案例:

舉個(gè)生活中的小例子:
我們一寢室人去上課,得等到1、2、3、4、5、6、7、8個(gè)人都出來,才可以鎖上寢室門吧。即當(dāng)計(jì)數(shù)器值為0時(shí),就可以執(zhí)行await的方法啦。

編碼步驟:

  1. CountDownLatch countDownLatch = new CountDownLatch(8);
  2. countDownLatch.countDown(); 一個(gè)線程出來一個(gè)人,計(jì)數(shù)器就 -1
  3. countDownLatch.await(); 阻塞的等待計(jì)數(shù)器歸零
  4. 執(zhí)行后續(xù)步驟

我們用代碼來模擬一下這個(gè)例子哈:

public class CountDownLatchDemo1 {public static void main(String[] args) {// 初始值8 有八個(gè)人需要出寢室門CountDownLatch countDownLatch = new CountDownLatch(8);for (int i = 1; i <= 8; i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + "出去啦");// 出去一個(gè)人計(jì)數(shù)器就減1countDownLatch.countDown();}, String.valueOf(i)).start();}try {countDownLatch.await(); // 阻塞等待計(jì)數(shù)器歸零} catch (InterruptedException e) {e.printStackTrace();}// 阻塞的操作 : 計(jì)數(shù)器  num++System.out.println(Thread.currentThread().getName() + "====寢室人都已經(jīng)出來了,關(guān)門向教室沖!!!====");}
}

小結(jié):

CountDownLatch使用給定的計(jì)數(shù)進(jìn)行初始化。 由于調(diào)用了countDown方法,每次-1, await方法會(huì)一直阻塞到當(dāng)前計(jì)數(shù)達(dá)到零,然后釋放所有等待線程,并且任何后續(xù)的await調(diào)用都會(huì)立即返回。 這是一種一次性現(xiàn)象——計(jì)數(shù)無法重置。 如果您需要重置計(jì)數(shù)的版本,請考慮使用CyclicBarrier 。
CountDownLatch一個(gè)有用屬性是它不需要調(diào)用countDown線程在繼續(xù)之前等待計(jì)數(shù)達(dá)到零,它只是阻止任何線程通過await,直到所有線程都可以通過。

CyclicBarrier(加法計(jì)數(shù)器)

概述:

CyclicBarrier 看英文單詞就可以看出大概就是循環(huán)阻塞的意思。所以還常稱為循環(huán)柵欄。
CyclicBarrier 主要方法有:

public class CyclicBarrier {private int dowait(boolean timed, long nanos); // 供await方法調(diào)用 判斷是否達(dá)到條件 可以往下執(zhí)行嗎//創(chuàng)建一個(gè)新的CyclicBarrier,它將在給定數(shù)量的參與方(線程)等待時(shí)觸發(fā),每執(zhí)行一次CyclicBarrier就累加1,達(dá)到了parties,就會(huì)觸發(fā)barrierAction的執(zhí)行public CyclicBarrier(int parties, Runnable barrierAction) ;//創(chuàng)建一個(gè)新的CyclicBarrier ,參數(shù)就是目標(biāo)障礙數(shù),它將在給定數(shù)量的參與方(線程)等待時(shí)觸發(fā),每次執(zhí)行 CyclicBarrier 一次障礙數(shù)會(huì)加一,如果達(dá)到了目標(biāo)障礙數(shù),才會(huì)執(zhí)行 cyclicBarrier.await()之后的語句public CyclicBarrier(int parties) //返回觸發(fā)此障礙所需的參與方數(shù)量。public int getParties()//等待,直到所有各方都在此屏障上調(diào)用了await 。// 如果當(dāng)前線程不是最后一個(gè)到達(dá)的線程,那么它會(huì)出于線程調(diào)度目的而被禁用并處于休眠狀態(tài).直到所有線程都調(diào)用了或者被中斷亦或者發(fā)生異常中斷退出public int await()// 基本同上 多了個(gè)等待時(shí)間 等待時(shí)間內(nèi)所有線程沒有完成,將會(huì)拋出一個(gè)超時(shí)異常public int await(long timeout, TimeUnit unit)//將障礙重置為其初始狀態(tài)。 public void reset()}

public CyclicBarrier(int parties):的構(gòu)造方法第一個(gè)參數(shù)是目標(biāo)障礙數(shù),每次執(zhí)行 CyclicBarrier 一次障礙數(shù)會(huì)加一,如果達(dá)到了目標(biāo)障礙數(shù),才會(huì)執(zhí)行 cyclicBarrier.await()之后 的語句??梢詫?CyclicBarrier 理解為加 1 操作。

public CyclicBarrier(int parties, Runnable barrierAction) :的構(gòu)造方法第一個(gè)參數(shù)是目標(biāo)障礙數(shù),每次執(zhí)行 CyclicBarrier 一次障礙數(shù)會(huì)加一,如果達(dá)到了目標(biāo)障礙數(shù),就會(huì)執(zhí)行我們傳入的Runnable;

案例:

我想大家多少玩過王者榮耀吧,里面不是有個(gè)鉆石奪寶嗎,抽201次必得榮耀水晶,這次讓我們用代碼來模擬一下吧。
編程步驟:

  1. 創(chuàng)建CyclicBarrier對象
    CyclicBarrier cyclicBarrier = new CyclicBarrier(count, new MyRunnable());

  2. 編寫業(yè)務(wù)代碼

  3. cyclicBarrier.await(); //在線程里面等待阻塞,累加1,達(dá)到最大值count時(shí),觸發(fā)我們傳入進(jìn)去MyRunnable執(zhí)行。

public class CyclicBarrierDemo1 {public static void main(String[] args) {// 第一個(gè)參數(shù):目標(biāo)障礙數(shù)  第二個(gè)參數(shù):一個(gè)Runnable任務(wù),當(dāng)達(dá)到目標(biāo)障礙數(shù)時(shí),就會(huì)執(zhí)行我們傳入的Runnable// 當(dāng)我們抽了201次的時(shí)候,就會(huì)執(zhí)行這個(gè)任務(wù)。CyclicBarrier cyclicBarrier = new CyclicBarrier(201,()->{System.out.println("恭喜你,已經(jīng)抽獎(jiǎng)201次,幸運(yùn)值已滿,下次抽獎(jiǎng)必中榮耀水晶!!!");});for (int i=1;i<=201;i++){final int count=i;new Thread(()->{System.out.println(Thread.currentThread().getName()+"抽獎(jiǎng)一次");try {cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}},String.valueOf(i)).start();}}// 這行代碼是重置計(jì)數(shù)cyclicBarrier.reset();// 這里是我又加了 一次循環(huán), 可以看到最后結(jié)果中輸出了兩次 "恭喜你"for (int i=1;i<=201;i++){final int count=i;new Thread(()->{System.out.println(Thread.currentThread().getName()+"抽獎(jiǎng)一次");try {cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}},String.valueOf(i)).start();}
}

小結(jié):

CyclicBarrier和CountDownLatch其實(shí)非常相似,CyclicBarrier表示加法,CountDownLatch表示減法。
區(qū)別還是有的:

  1. CyclicBarrier只能夠喚醒一個(gè)任務(wù),CountDownLatch可以喚起多個(gè)任務(wù)。
  2. CyclicBarrier可以重置,重新使用,但是CountDownLatch的值等于0時(shí),就不可重復(fù)用了。

Semaphore( 信號燈)

概述:

Semaphore:信號量通常用于限制可以訪問某些(物理或邏輯)資源的線程數(shù)。
使用場景:
限制資源,如搶位置、限流等。

案例:

【例子】:
不知道大家有沒有過在網(wǎng)吧搶電腦打游戲的那種經(jīng)歷,小時(shí)候,平常便宜點(diǎn)的網(wǎng)吧都比較小,而且也比較少,特別多的人去,去晚了的人就只有站在那里看,等別人下機(jī)才能上網(wǎng)。
這次的例子就是:網(wǎng)吧有十臺高配置打游戲的電腦,有20個(gè)小伙伴想要上網(wǎng)。
我們用代碼來模擬一下:
編程步驟:

  1. 創(chuàng)建信號燈
    Semaphore semaphore = new Semaphore(10); // 5個(gè)位置

  2. 等待獲取信號燈
    semaphore.acquire();//等待獲取許可證

  3. 業(yè)務(wù)代碼

  4. 釋放信號
    semaphore.release();//釋放資源,女朋友來找了,下機(jī)下機(jī),陪女朋友去了,那么就要釋放這臺電腦啦

public class SemaphoreDemo1 {public static void main(String[] args) {// 10臺電腦Semaphore semaphore = new Semaphore(10);// 20 個(gè)小伙伴想要上網(wǎng)for (int i = 1; i <= 20; i++) {new Thread(() -> {try {//等待獲取許可證semaphore.acquire();System.out.println(Thread.currentThread().getName() + "搶到了電腦");//搶到的小伙伴,迅速就開打啦 這里就模擬個(gè)時(shí)間哈,TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();} finally {//打完幾把游戲的小伙伴 女朋友來找了 溜啦溜啦 希望大家都有人陪伴System.out.println("女朋友來找,"+Thread.currentThread().getName() + "離開了");semaphore.release();//釋放資源,離開了就要把電腦讓給別人啦。}}, String.valueOf(i)).start();}}
}

小結(jié):

  1. 在獲得一個(gè)項(xiàng)目之前,每個(gè)線程必須從信號量中獲得一個(gè)許可,以保證一個(gè)項(xiàng)目可供使用。 當(dāng)線程完成該項(xiàng)目時(shí),它會(huì)返回到池中,并且將許可返回給信號量,允許另一個(gè)線程獲取該項(xiàng)目。 請注意,調(diào)用acquire時(shí)不會(huì)持有同步鎖,因?yàn)檫@會(huì)阻止項(xiàng)目返回到池中。 信號量封裝了限制訪問池所需的同步,與維護(hù)池本身一致性所需的任何同步分開。
  2. 初始化為 1 的信號量,并且使用時(shí)最多只有一個(gè)許可可用,可以用作互斥鎖。 這通常被稱為二進(jìn)制信號量,因?yàn)樗挥袃煞N狀態(tài):一種許可可用,或零許可可用。 以這種方式使用時(shí),二進(jìn)制信號量具有屬性(與許多java.util.concurrent.locks.Lock實(shí)現(xiàn)不同),即“鎖”可以由所有者以外的線程釋放(因?yàn)樾盘柫繘]有所有權(quán)的概念)。 這在某些特定上下文中很有用,例如死鎖恢復(fù)。
  3. 此類的構(gòu)造函數(shù)可以選擇接受公平參數(shù)。 當(dāng)設(shè)置為 false 時(shí),此類不保證線程獲取許可的順序。 當(dāng)公平性設(shè)置為真時(shí),信號量保證調(diào)用任何acquire方法的線程被選擇以按照它們對這些方法的調(diào)用的處理順序(先進(jìn)先出;FIFO)獲得許可。
  4. 通常,用于控制資源訪問的信號量應(yīng)初始化為公平的,以確保沒有線程因訪問資源而餓死。 當(dāng)使用信號量進(jìn)行其他類型的同步控制時(shí),非公平排序的吞吐量優(yōu)勢通常超過公平性考慮。
  5. 內(nèi)存一致性影響:在調(diào)用“釋放”方法(如release()之前線程中的操作發(fā)生在另一個(gè)線程中成功的“獲取”方法(如acquire()之后的操作之前。

簡單講述 | Phaser & Exchanger

Phaser

Phaser一種可重用的同步屏障,功能上類似于CyclicBarrier和CountDownLatch,但使用上更為靈活。非常適用于在多線程環(huán)境下同步協(xié)調(diào)分階段計(jì)算任務(wù)(Fork/Join框架中的子任務(wù)之間需同步時(shí),優(yōu)先使用Phaser)

//默認(rèn)的構(gòu)造方法,初始化注冊的線程數(shù)量為0,可以動(dòng)態(tài)注冊
Phaser();
//指定了線程數(shù)量的構(gòu)造方法
Phaser(int parties);
//添加一個(gè)注冊者  向此移相器添加一個(gè)新的未到達(dá)方。 如果正在進(jìn)行對onAdvance調(diào)用,則此方法可能會(huì)在返回之前等待其完成。
register();
//添加指定數(shù)量的注冊者  將給定數(shù)量的新未到達(dá)方添加到此移相器(移相器就是Phaser)。
bulkRegister(int parties);
// 到達(dá)屏障點(diǎn)直接執(zhí)行 無需等待其他人到達(dá)。
arrive();
//到達(dá)屏障點(diǎn)后,也必須等待其他所有注冊者到達(dá)這個(gè)屏障點(diǎn)才能繼續(xù)下一步
arriveAndAwaitAdvance();
//到達(dá)屏障點(diǎn),把自己注銷了,不用等待其他的注冊者到達(dá)
arriveAndDeregister();
//多個(gè)線程達(dá)到注冊點(diǎn)之后,會(huì)回調(diào)這個(gè)方法,可以做一些邏輯的補(bǔ)充
onAdvance(int phase, int registeredParties);
package com.crush.juc05;import java.util.concurrent.Phaser;public class PhaserDemo {private static Phaser phaser = new MyPhaser();//自定義一個(gè)移相器來自定義輸出static class MyPhaser extends Phaser {/*** @deprecated 在即將到來的階段提前時(shí)執(zhí)行操作并控制終止的可覆蓋方法。 此方法在推進(jìn)此移相器的一方到達(dá)時(shí)調(diào)用(當(dāng)所有其他等待方處于休眠狀態(tài)時(shí))。*             如果此方法返回true ,則此移相器將在提前時(shí)設(shè)置為最終終止?fàn)顟B(tài),并且對isTerminated后續(xù)調(diào)用將返回 true。* @param phase 進(jìn)入此方法的當(dāng)前階段號,在此移相器前進(jìn)之前* @param registeredParties 當(dāng)前注冊方的數(shù)量* @return*/@Overrideprotected boolean onAdvance(int phase, int registeredParties) {if (phase == 0) {System.out.println("所有人都到達(dá)了網(wǎng)吧,準(zhǔn)備開始開黑!!!");return false;} else if (phase == 1) {System.out.println("大家都同意,一起去次燒烤咯!!!");return false;} else if (phase == 2) {System.out.println("大家一起回寢室!!!");return true;}return true;}}//構(gòu)建一個(gè)線程任務(wù)static class DoSomeThing implements Runnable {@Overridepublic void run() {/*** 向此移相器添加一個(gè)新的未到達(dá)方*/phaser.register();System.out.println(Thread.currentThread().getName() + "從家里出發(fā),準(zhǔn)備去學(xué)校后街上網(wǎng)開黑!!!");phaser.arriveAndAwaitAdvance();System.out.println(Thread.currentThread().getName() + "上著上著餓了,說去次燒烤嗎?");phaser.arriveAndAwaitAdvance();System.out.println(Thread.currentThread().getName() + "燒烤次完了");phaser.arriveAndAwaitAdvance();}}public static void main(String[] args) throws Exception {DoSomeThing thing = new DoSomeThing();new Thread(thing, "小明").start();new Thread(thing, "小王").start();new Thread(thing, "小李").start();}
}
/*** 小李從家里出發(fā),準(zhǔn)備去學(xué)校后街上網(wǎng)開黑!!!* 小王從家里出發(fā),準(zhǔn)備去學(xué)校后街上網(wǎng)開黑!!!* 小明從家里出發(fā),準(zhǔn)備去學(xué)校后街上網(wǎng)開黑!!!* 所有人都到達(dá)了網(wǎng)吧,準(zhǔn)備開始開黑!!!* 小李上著上著餓了,說去次燒烤嗎?* 小明上著上著餓了,說去次燒烤嗎?* 小王上著上著餓了,說去次燒烤嗎?* 大家都同意,一起去次燒烤咯!!!* 小明燒烤次完了* 小李燒烤次完了* 小王燒烤次完了* 大家一起回寢室!!!*/

注意:這里只是做了簡單的一個(gè)使用,更深入的了解,我暫時(shí)也沒有,想要研究可以去查一查。

Exchanger

Exchanger允許兩個(gè)線程在某個(gè)匯合點(diǎn)交換對象,在某些管道設(shè)計(jì)時(shí)比較有用。
Exchanger提供了一個(gè)同步點(diǎn),在這個(gè)同步點(diǎn),一對線程可以交換數(shù)據(jù)。每個(gè)線程通過exchange()方法的入口提供數(shù)據(jù)給他的伙伴線程,并接收他的伙伴線程提供的數(shù)據(jù)并返回。

當(dāng)兩個(gè)線程通過Exchanger交換了對象,這個(gè)交換對于兩個(gè)線程來說都是安全的。Exchanger可以認(rèn)為是 SynchronousQueue 的雙向形式,在運(yùn)用到遺傳算法和管道設(shè)計(jì)的應(yīng)用中比較有用。

這個(gè)的使用我在Dubbo中的總體架構(gòu)圖中看到了它的身影。

在這里插入圖片描述

十二、總結(jié)

本文主要介紹了JUC是什么、JUC框架結(jié)構(gòu)概述、常用到的類匯總、相關(guān)名詞、重點(diǎn)介紹了Lock鎖,Callable接口、Feature接口以及JUC三大常用工具類,希望對大家有幫助,歡迎評論區(qū)留言。

http://www.risenshineclean.com/news/49496.html

相關(guān)文章:

  • 買一個(gè)app軟件要多少錢seo教學(xué)
  • 手機(jī)wap網(wǎng)站模板沈陽百度推廣優(yōu)化
  • 麥包包網(wǎng)站建設(shè)特點(diǎn)seo課程培訓(xùn)
  • 南陽手機(jī)網(wǎng)站制作公司想做網(wǎng)絡(luò)推廣貴不
  • 怎么用網(wǎng)站模板廈門關(guān)鍵詞排名seo
  • 網(wǎng)站設(shè)計(jì)與建設(shè)工作室百度地圖導(dǎo)航2021最新版
  • 廈門市建設(shè)局網(wǎng)站公布網(wǎng)頁制作流程
  • wang域名的網(wǎng)站種子搜索神器網(wǎng)頁版
  • 保定網(wǎng)站建設(shè)與seo搜索關(guān)鍵詞排名提升
  • 長沙3合1網(wǎng)站建設(shè)星巴克網(wǎng)絡(luò)營銷案例分析
  • 織夢怎么做企業(yè)網(wǎng)站廣西百度seo
  • 畢業(yè)設(shè)計(jì)網(wǎng)站開發(fā)任務(wù)安排哪家公司建設(shè)網(wǎng)站好
  • 影視網(wǎng)站模板怎么做青島 google seo
  • 東營網(wǎng)站建設(shè)方案策劃太原seo排名收費(fèi)
  • 網(wǎng)站建設(shè)需要什么方案好視通視頻會(huì)議app下載安裝
  • 網(wǎng)站總類新開傳奇網(wǎng)站發(fā)布站
  • wordpress建站難不難網(wǎng)頁設(shè)計(jì)html代碼大全
  • dw做網(wǎng)站鼠標(biāo)經(jīng)過圖像seo優(yōu)化團(tuán)隊(duì)
  • 云南網(wǎng)站建設(shè)營銷肇慶seo排名
  • 旅游類網(wǎng)站怎么做百度官方免費(fèi)下載
  • linux網(wǎng)站服務(wù)器配置自媒體營銷代理
  • 大氣的外貿(mào)公司名字福州短視頻seo獲客
  • 欣賞別人做的網(wǎng)站網(wǎng)站免費(fèi)搭建
  • 學(xué)習(xí)電子商務(wù)網(wǎng)站建設(shè)與管理的感想網(wǎng)站備案查詢
  • 手機(jī)網(wǎng)站免費(fèi)模板下載整合營銷理論主要是指
  • wordpress注冊完成請檢查電子郵件網(wǎng)絡(luò)優(yōu)化排名培訓(xùn)
  • 怎樣建立一個(gè)免費(fèi)的網(wǎng)站桔子seo工具
  • 如何 建公司網(wǎng)站win10系統(tǒng)優(yōu)化
  • 建設(shè)政府網(wǎng)站網(wǎng)站建設(shè)是干什么的
  • 武漢網(wǎng)站制作誰家好優(yōu)化網(wǎng)站seo方案