日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JAVA高并发编程

發布時間:2023/12/9 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JAVA高并发编程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
    • synchronized 關鍵字
      • 同步方法
      • 同步代碼塊
      • 鎖的底層實現
      • 鎖的種類
      • volatile 關鍵字
      • wait?ify
      • AtomicXxx 類型組
      • CountDownLatch 門閂
      • 鎖的重入
      • ReentrantLock
    • 同步容器
      • Map/Set
      • List
      • Queue
    • ThreadPool&Executor
      • Executor
      • ExecutorService
      • Future
      • Callable
      • Executors
      • FixedThreadPool
      • CachedThreadPool
      • ScheduledThreadPool
      • SingleThreadExceutor
      • ForkJoinPool
      • WorkStealingPool
      • ThreadPoolExecutor

?

synchronized 關鍵字

可用來給對象和方法或者代碼塊加鎖,當它鎖定一個方法或者一個代碼塊的時候,同一時刻最多只有一個線程執行這段代碼??赡苕i對象包括: this, 臨界資源對象,Class 類對象

同步方法

同步方法鎖定的是當前對象。當多線程通過同一個對象引用多次調用當前同步方法時, 需同步執行。

public synchronized void test(){System.out.println("測試一下");}
  • 1
  • 2
  • 3

同步代碼塊

同步代碼塊的同步粒度更加細致,是商業開發中推薦的編程方式??梢远ㄎ坏骄唧w的同步位置,而不是簡單的將方法整體實現同步邏輯。在效率上,相對更高。?
鎖定臨界對象?
同步代碼塊在執行時,是鎖定 object 對象。當多個線程調用同一個方法時,鎖定對象不變的情況下,需同步執行。

public void test(){synchronized(o){System.out.println("測試一下");}}
  • 1
  • 2
  • 3
  • 4
  • 5

鎖定當前對象

public void test(){synchronized(this){System.out.println("測試一下");}}
  • 1
  • 2
  • 3
  • 4
  • 5

鎖的底層實現

Java 虛擬機中的同步(Synchronization)基于進入和退出管程(Monitor)對象實現。同步方法 并不是由 monitor enter 和 monitor exit 指令來實現同步的,而是由方法調用指令讀取運行時常量池中方法的 ACC_SYNCHRONIZED 標志來隱式實現的。?

對象頭:存儲對象的 hashCode、鎖信息或分代年齡或 GC 標志,類型指針指向對象的類元數據,JVM 通過這個指針確定該對象是哪個類的實例等信息。?
實例變量:存放類的屬性數據信息,包括父類的屬性信息?
填充數據:由于虛擬機要求對象起始地址必須是 8 字節的整數倍。填充數據不是必須存在的,僅僅是為了字節對齊?
當在對象上加鎖時,數據是記錄在對象頭中。當執行 synchronized 同步方法或同步代碼塊時,會在對象頭中記錄鎖標記,鎖標記指向的是 monitor 對象(也稱為管程或監視器鎖) 的起始地址。每個對象都存在著一個 monitor 與之關聯,對象與其 monitor 之間的關系有存在多種實現方式,如 monitor 可以與對象一起創建銷毀或當線程試圖獲取對象鎖時自動生成,但當一個 monitor 被某個線程持有后,它便處于鎖定狀態。?
在 Java 虛擬機(HotSpot)中,monitor 是由 ObjectMonitor 實現的。?
ObjectMonitor 中有兩個隊列,_WaitSet 和 _EntryList,以及_Owner 標記。其中_WaitSet 是用于管理等待隊列(wait)線程的,_EntryList 是用于管理鎖池阻塞線程的,_Owner 標記用于記錄當前執行線程。線程狀態圖如下:

當多線程并發訪問同一個同步代碼時,首先會進入_EntryList,當線程獲取鎖標記后,?
monitor 中的_Owner 記錄此線程,并在 monitor 中的計數器執行遞增計算(+1),代表鎖定,其他線程在_EntryList 中繼續阻塞。若執行線程調用 wait 方法,則 monitor 中的計數器執行賦值為 0 計算,并將_Owner 標記賦值為 null,代表放棄鎖,執行線程進如_WaitSet 中阻塞。若執行線程調用 notify/notifyAll 方法,_WaitSet 中的線程被喚醒,進入_EntryList 中阻塞,等待獲取鎖標記。若執行線程的同步代碼執行結束,同樣會釋放鎖標記,monitor 中的_Owner 標記賦值為 null,且計數器賦值為 0 計算。

鎖的種類

Java 中鎖的種類大致分為偏向鎖,自旋鎖,輕量級鎖,重量級鎖。?
鎖的使用方式為:先提供偏向鎖,如果不滿足的時候,升級為輕量級鎖,再不滿足,升級為重量級鎖。自旋鎖是一個過渡的鎖狀態,不是一種實際的鎖類型。?
鎖只能升級,不能降級。

重量級鎖

在鎖的底層實現中解釋的就是重量級鎖。

偏向鎖

是一種編譯解釋鎖。如果代碼中不可能出現多線程并發爭搶同一個鎖的時候,JVM 編譯代碼,解釋執行的時候,會自動的放棄同步信息。消除 synchronized 的同步代碼結果。使用鎖標記的形式記錄鎖狀態。在 Monitor 中有變量 ACC_SYNCHRONIZED。當變量值使用的時候, 代表偏向鎖鎖定??梢员苊怄i的爭搶和鎖池狀態的維護。提高效率。

輕量級鎖?
過渡鎖。當偏向鎖不滿足,也就是有多線程并發訪問,鎖定同一個對象的時候,先提升為輕量級鎖。也是使用標記 ACC_SYNCHRONIZED 標記記錄的。ACC_UNSYNCHRONIZED 標記記錄未獲取到鎖信息的線程。就是只有兩個線程爭搶鎖標記的時候,優先使用輕量級鎖。?
兩個線程也可能出現重量級鎖。

自旋鎖?
是一個過渡鎖,是偏向鎖和輕量級鎖的過渡。?
當獲取鎖的過程中,未獲取到。為了提高效率,JVM 自動執行若干次空循環,再次申請鎖,而不是進入阻塞狀態的情況。稱為自旋鎖。自旋鎖提高效率就是避免線程狀態的變更。

volatile 關鍵字

變量的線程可見性。在 CPU 計算過程中,會將計算過程需要的數據加載到 CPU 計算緩存中,當 CPU 計算中斷時,有可能刷新緩存,重新讀取內存中的數據。在線程運行的過程中,如果某變量被其他線程修改,可能造成數據不一致的情況,從而導致結果錯誤。而 volatile 修飾的變量是線程可見的,當 JVM 解釋 volatile 修飾的變量時,會通知 CPU,在計算過程中, 每次使用變量參與計算時,都會檢查內存中的數據是否發生變化,而不是一直使用 CPU 緩存中的數據,可以保證計算結果的正確。?
volatile 只是通知底層計算時,CPU 檢查內存數據,而不是讓一個變量在多個線程中同步。

volatile int count = 0;
  • 1

wait?ify

AtomicXxx 類型組

原子類型。?
在 concurrent.atomic 包中定義了若干原子類型,這些類型中的每個方法都是保證了原子操作的。多線程并發訪問原子類型對象中的方法,不會出現數據錯誤。在多線程開發中,如果某數據需要多個線程同時操作,且要求計算原子性,可以考慮使用原子類型對象。

AtomicInteger count = new AtomicInteger(0);void m(){count.incrementAndGet();}
  • 1
  • 2
  • 3
  • 4

注意:原子類型中的方法是保證了原子操作,但多個方法之間是沒有原子性的。如:

AtomicInteger i = new AtomicInteger(0); if(i.get() != 5){i.incrementAndGet(); }
  • 1
  • 2
  • 3
  • 4

在上述代碼中,get 方法和 incrementAndGet 方法都是原子操作,但復合使用時,無法保證原子性,仍舊可能出現數據錯誤。

CountDownLatch 門閂

門閂是 concurrent 包中定義的一個類型,是用于多線程通訊的一個輔助類型。?
門閂相當于在一個門上加多個鎖,當線程調用 await 方法時,會檢查門閂數量,如果門

閂數量大于 0,線程會阻塞等待。當線程調用 countDown 時,會遞減門閂的數量,當門閂數量為 0 時,await 阻塞線程可執行。

CountDownLatch latch = new CountDownLatch(5);void m1(){try {latch.await();// 等待門閂開放。} catch (InterruptedException e) {e.printStackTrace();}System.out.println("m1() method");}void m2(){for(int i = 0; i < 10; i++){if(latch.getCount() != 0){System.out.println("latch count : " + latch.getCount());latch.countDown(); // 減門閂上的鎖。}try {TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("m2() method : " + i);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

鎖的重入

在 Java 中,同步鎖是可以重入的。只有同一線程調用同步方法或執行同步代碼塊,對同一個對象加鎖時才可重入。?
當線程持有鎖時,會在 monitor 的計數器中執行遞增計算,若當前線程調用其他同步代碼,且同步代碼的鎖對象相同時,monitor 中的計數器繼續遞增。每個同步代碼執行結束,?
monitor 中的計數器都會遞減,直至所有同步代碼執行結束,monitor 中的計數器為 0 時,釋放鎖標記,_Owner 標記賦值為 null。

ReentrantLock

重入鎖,建議應用的同步方式。相對效率比 synchronized 高。量級較輕。?
synchronized 在 JDK1.5 版本開始,嘗試優化。到 JDK1.7 版本后,優化效率已經非常好了。在絕對效率上,不比 reentrantLock 差多少。?
使用重入鎖,必須必須必須手工釋放鎖標記。一般都是在 finally 代碼塊中定義釋放鎖標記的 unlock 方法。

公平鎖

private static ReentrantLock lock = new ReentrantLock(true);public void run(){for(int i = 0; i < 5; i++){lock.lock();try{System.out.println(Thread.currentThread().getName() + " get lock");}finally{lock.unlock();}}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

8ThreadLocal

remove 問題?

同步容器

解決并發情況下的容器線程安全問題的。給多線程環境準備一個線程安全的容器對象。線程安全的容器對象: Vector, Hashtable。線程安全容器對象,都是使用 synchronized?
方法實現的。?
concurrent 包中的同步容器,大多數是使用系統底層技術實現的線程安全。類似 native。?
Java8 中使用 CAS。

Map/Set

ConcurrentHashMap/ConcurrentHashSet

底層哈希實現的同步 Map(Set)。效率高,線程安全。使用系統底層技術實現線程安全。量級較 synchronized 低。key 和 value 不能為 null。

ConcurrentSkipListMap/ConcurrentSkipListSet

底層跳表(SkipList)實現的同步 Map(Set)。有序,效率比 ConcurrentHashMap 稍低。

List

CopyOnWriteArrayList

寫時復制集合。寫入效率低,讀取效率高。每次寫入數據,都會創建一個新的底層數組。

Queue

ConcurrentLinkedQueue

基礎鏈表同步隊列。

LinkedBlockingQueue?
阻塞隊列,隊列容量不足自動阻塞,隊列容量為 0 自動阻塞。

ArrayBlockingQueue?
底層數組實現的有界隊列。自動阻塞。根據調用 API(add/put/offer)不同,有不同特性。?
當容量不足的時候,有阻塞能力。?
add 方法在容量不足的時候,拋出異常。?
put 方法在容量不足的時候,阻塞等待。?
offer 方法,?
單參數 offer 方法,不阻塞。容量不足的時候,返回 false。當前新增數據操作放棄。三參數 offer 方法(offer(value,times,timeunit)),容量不足的時候,阻塞 times 時長(單?
位為 timeunit),如果在阻塞時長內,有容量空閑,新增數據返回 true。如果阻塞時長范圍?
內,無容量空閑,放棄新增數據,返回 false。

DelayQueue?
延時隊列。根據比較機制,實現自定義處理順序的隊列。常用于定時任務。如:定時關機。

LinkedTransferQueue?
轉移隊列,使用 transfer 方法,實現數據的即時處理。沒有消費者,就阻塞。

SynchronusQueue?
同步隊列,是一個容量為 0 的隊列。是一個特殊的 TransferQueue。必須現有消費線程等待,才能使用的隊列。?
add 方法,無阻塞。若沒有消費線程阻塞等待數據,則拋出異常。?
put 方法,有阻塞。若沒有消費線程阻塞等待數據,則阻塞。

ThreadPool&Executor

Executor

線程池頂級接口。?
常用方法 - void execute(Runnable)?
作用是: 啟動線程任務的。

ExecutorService

Executor 接口的子接口。?
常見方法 - Future submit(Callable), Future submit(Runnable)

Future

未來結果,代表線程任務執行結束后的結果。

Callable

可執行接口。?
接口方法 : Object call();相當于 Runnable 接口中的 run 方法。區別為此方法有返回值。不能拋出已檢查異常。?
和 Runnable 接口的選擇 - 需要返回值或需要拋出異常時,使用 Callable,其他情況可任意選擇。

Executors

工具類型。為 Executor 線程池提供工具方法。類似 Arrays,Collections 等工具類型的功用。

FixedThreadPool

容量固定的線程池?
queued tasks - 任務隊列?
completed tasks - 結束任務隊列

CachedThreadPool

緩存的線程池。容量不限(Integer.MAX_VALUE)。自動擴容。默認線程空閑 60 秒,自動銷毀。

ScheduledThreadPool

計劃任務線程池??梢愿鶕媱澴詣訄绦腥蝿盏木€程池。

SingleThreadExceutor

單一容量的線程池。

ForkJoinPool

分支合并線程池(mapduce 類似的設計思想)。適合用于處理復雜任務。初始化線程容量與 CPU 核心數相關。?
線程池中運行的內容必須是 ForkJoinTask 的子類型(RecursiveTask,RecursiveAction)。

WorkStealingPool

JDK1.8 新增的線程池。工作竊取線程池。當線程池中有空閑連接時,自動到等待隊列中竊取未完成任務,自動執行。?
初始化線程容量與 CPU 核心數相關。此線程池中維護的是精靈線程。?
ExecutorService.newWorkStealingPool();

ThreadPoolExecutor

線程池底層實現。除 ForkJoinPool 外,其他常用線程池底層都是使用 ThreadPoolExecutor?
實現的。?
public ThreadPoolExecutor?
(int corePoolSize, // 核心容量

int maximumPoolSize, // 最大容量?
long keepAliveTime, // 生命周期,0 為永久?
TimeUnit unit, // 生命周期單位?
BlockingQueue workQueue // 任務隊列,阻塞隊列。?
);

總結

以上是生活随笔為你收集整理的JAVA高并发编程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。