成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Java并發之同步器設計

開發 后端
計算機中通過設計同步器來協調進程(線程)之間執行順序。同步器作用就像登機安檢人員一樣可以協調旅客按順序通過。

在 Java并發之內存模型了解到多進程(線程)讀取共享資源的時候存在競爭條件。

計算機中通過設計同步器來協調進程(線程)之間執行順序。同步器作用就像登機安檢人員一樣可以協調旅客按順序通過。

在Java中,同步器可以理解為一個對象,它根據自身狀態協調線程的執行順序。比如鎖(Lock),信號量(Semaphore),屏障(CyclicBarrier),阻塞隊列(Blocking Queue)。

這些同步器在功能設計上有所不同,但是內部實現上有共通的地方。

同步器

同步器的設計一般包含幾個方面:狀態變量設計(同步器內部狀態),訪問條件設定,狀態更新,等待方式,通知策略。

訪問條件是控制線程是否能執行(訪問共享對象)的條件,它往往與狀態變量緊密相關。而通知策略是線程釋放鎖定狀態后通知其它等待線程的方式,一般有以下幾種情況

  •  通知所有等待的線程。
  •  通知1個隨機的N個等待線程。
  •  通知1個特定的N個等待線程

看下面例子,通過鎖方式的同步器 

  1. public class Lock{  
  2.   // 狀態變量 isLocked  
  3.   private boolean isLocked = false 
  4.   public synchronized void lock() throws InterruptedException{  
  5.     // 訪問條件 當isLocked=false 時獲得訪問權限否則等待  
  6.     while(isLocked){  
  7.       // 阻塞等待  
  8.       wait();  
  9.     }  
  10.     //狀態更新 線程獲得訪問權限  
  11.     isLocked = true 
  12.   }  
  13.   public synchronized void unlock(){  
  14.     //狀態更新 線程釋放訪問權限  
  15.     isLocked = false 
  16.     // 通知策略 object.notify | object.notifyAll  
  17.     notify();   
  18.   }  

我們用計數信號量控制同時執行操作活動數。這里模擬一個連接池。 

  1. public class PoolSemaphore {  
  2.       // 狀態變量 actives 計數器  
  3.     private int actives = 0 
  4.     private int max;  
  5.     public PoolSemaphore(int max) {  
  6.         this.max = max;  
  7.     }  
  8.     public synchronized void acquire() throws InterruptedException {  
  9.         //訪問條件 激活數小于最大限制時,獲得訪問權限否則等待  
  10.         while (this.actives == max) wait();  
  11.         //狀態更新 線程獲得訪問權限  
  12.         this.actives++;  
  13.         // 通知策略 object.notify | object.notifyAll  
  14.         this.notify();  
  15.     }  
  16.     public synchronized void release() throws InterruptedException {  
  17.         //訪問條件 激活數不為0時,獲得訪問權限否則等待  
  18.         while (this.actives == 0) wait();  
  19.          //狀態更新 線程獲得訪問權限  
  20.         this.actives--;  
  21.         // 通知策略 object.notify | object.notifyAll  
  22.         this.notify();  
  23.     }  

原子指令

同步器設計里面,最重要的操作邏輯是“如果滿足條件,以更新狀態變量來標志線程獲得或釋放訪問權限”,該操作應具備原子性。

比如test-and-set 計算機原子指令,意思是進行條件判斷滿足則設置新值。 

  1. function Lock(boolean *lock) {   
  2.     while (test_and_set(lock) == 1);   

另外還有很多原子指令 fetch-and-add compare-and-swap,注意這些指令需硬件支持才有效。

同步操作中,利用計算機原子指令,可以避開鎖,提升效率。java中沒有 test-and-set 的支持,不過 java.util.concurrent.atomic 給我們提供了很多原子類API,里面支持了 getAndSet 和compareAndSet 操作。

看下面例子,主要在區別是等待方式不一樣,上面是通過wait()阻塞等待,下面是無阻塞循環。 

  1. public class Lock{  
  2.   // 狀態變量 isLocked  
  3.   private AtomicBoolean isLocked = new AtomicBoolean(false);  
  4.   public void lock() throws InterruptedException{  
  5.     // 等待方式 變為自旋等待  
  6.     while(!isLocked.compareAndSet(false, true));  
  7.     //狀態更新 線程獲得訪問權限  
  8.     isLocked.set(true);  
  9.   }  
  10.   public synchronized void unlock(){  
  11.     //狀態更新 線程釋放訪問權限  
  12.     isLocked.set(false);  
  13.   }  

關于阻塞擴展說明

阻塞意味著需要將進程或線程狀態進行轉存,以便還原后恢復執行。這種操作是昂貴繁重,而線程基于進程之上相對比較輕量。線程的阻塞在不同編程平臺實現方式也有所不同,像Java是基于JVM運行,所以它由JVM完成實現。

在《Java Concurrency in Practice》中,作者提到

競爭性同步可能需要OS活動,這增加了成本。當爭用鎖時,未獲取鎖的線程必須阻塞。 JVM可以通過旋轉等待(反復嘗試獲取鎖直到成功)來實現阻塞,也可以通過操作系統掛起阻塞的線程來實現阻塞。哪種效率更高取決于上下文切換開銷與鎖定可用之前的時間之間的關系。對于短暫的等待,最好使用自旋等待;對于長時間的等待,最好使用暫停。一些JVM基于對過去等待時間的分析數據來自適應地在這兩者之間進行選擇,但是大多數JVM只是掛起線程等待鎖定。

從上面可以看出JVM實現阻塞兩種方式

  •  旋轉等待(spin-waiting),簡單理解是不暫停執行,以循環的方式等待,適合短時間場景。
  •  通過操作系統掛起線程。

JVM中通過 -XX: +UseSpinning 開啟旋轉等待, -XX: PreBlockSpi =10指定最大旋轉次數。

AQS

AQS是AbstractQueuedSynchronizer簡稱。本節對AQS只做簡單闡述,并不全面。

java.util.concurrent包中的 ReentrantLock,CountDownLatch,Semaphore,CyclicBarrier等都是基于是AQS同步器實現。

狀態變量 是用 int state 來表示,狀態的獲取與更新通過以下API操作。     

  1. int getState()  
  2.     void setState(int newState)  
  3.     boolean compareAndSetState(int expect, int update) 

該狀態值在不同API中有不同表示意義。比如ReentrantLock中表示持有鎖的線程獲取鎖的次數,Semaphore表示剩余許可數。

關于等待方式和通知策略的設計

AQS通過維護一個FIFO同步隊列(Sync queue)來進行同步管理。當多線程爭用共享資源時被阻塞入隊。而線程阻塞與喚醒是通過 LockSupport.park/unpark API實現。

它定義了兩種資源共享方式。

  •  Exclusive(獨占,只有一個線程能執行,如ReentrantLock)
  •  Share(共享,多個線程可同時執行,如Semaphore/CountDownLatch)

每個節點包含waitStatus(節點狀態),prev(前繼),next(后繼),thread(入隊時線程),nextWaiter(condition隊列的后繼節點)

waitStatus 有以下取值。

  •  CANCELLED(1) 表示線程已取消。當發生超時或中斷,節點狀態變為取消,之后狀態不再改變。
  •  SIGNAL(-1) 表示后繼節點等待前繼的喚醒。后繼節點入隊時,會將前繼狀態更新為SIGNAL。
  •  CONDITION(-2) 表示線程在Condition queue 里面等待。當其他線程調用了Condition.signal()方法后,CONDITION狀態的節點將從 Condition queue 轉移到 Sync queue,等待獲取鎖。
  •  PROPAGATE(-3) 在共享模式下,當前節點釋放后,確保有效通知后繼節點。
  •  (0) 節點加入隊列時的默認狀態。

AQS 幾個關鍵 API

  •  tryAcquire(int) 獨占方式下,嘗試去獲取資源。成功返回true,否則false。
  •  tryRelease(int) 獨占方式下,嘗試釋放資源,成功返回true,否則false。
  •  tryAcquireShared(int) 共享方式下,嘗試獲取資源。返回負數為失敗,零和正數為成功并表示剩余資源。
  •  tryReleaseShared(int) 共享方式下,嘗試釋放資源,如果釋放后允許喚醒后續等待節點返回true,否則false。
  •  isHeldExclusively() 判斷線程是否正在獨占資源。

acquire(int arg)   

  1. public final void acquire(int arg) {  
  2.         if (  
  3.           // 嘗試直接去獲取資源,如果成功則直接返回  
  4.           !tryAcquire(arg)  
  5.             &&  
  6.             //線程阻塞在同步隊列等待獲取資源。等待過程中被中斷,則返回true,否則false  
  7.             acquireQueued(  
  8.               // 標記該線程為獨占方式,并加入同步隊列尾部。  
  9.               addWaiter(Node.EXCLUSIVE), arg)   
  10.            )  
  11.             selfInterrupt();  
  12.     } 

release(int arg)   

  1. public final boolean release(int arg) {  
  2.          // 嘗試釋放資源  
  3.        if (tryRelease(arg)) {  
  4.            Node h = head 
  5.            if (h != null && h.waitStatus != 0)  
  6.              // 喚醒下一個線程(后繼節點)  
  7.              unparkSuccessor(h);  
  8.            return true;  
  9.        }  
  10.        return false;  
  11.    }  
  1. private void unparkSuccessor(Node node) {  
  2.         ....  
  3.               Node s = node.next; // 找到后繼節點  
  4.         if (s == null || s.waitStatus > 0) {//無后繼或節點已取消  
  5.             s = null 
  6.            // 找到有效的等待節點   
  7.             for (Node t = tail; t != null && t != node; tt = t.prev)  
  8.                 if (t.waitStatus <= 0)  
  9.                     s = t 
  10.         }  
  11.         if (s != null)  
  12.             LockSupport.unpark(s.thread); // 喚醒 
  13.     } 

總結

本文記錄并發編程中同步器設計的一些共性特征。并簡單介紹了Java中的AQS。 

 

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2012-06-05 02:12:55

Java多線程

2020-11-16 08:11:32

ReentrantLo

2021-04-12 08:21:48

AQSjavajvm

2021-04-13 14:07:22

JUC解析AQS抽象

2025-02-17 02:00:00

Monitor機制代碼

2024-02-01 13:03:00

AI模型

2012-03-09 10:44:11

Java

2017-09-19 14:53:37

Java并發編程并發代碼設計

2024-10-18 11:29:15

2023-08-16 12:34:16

同步備份異步備份

2020-09-04 10:29:47

Java線程池并發

2013-08-07 10:46:07

Java并發編程

2025-01-13 09:24:32

2024-11-13 15:09:57

Java線程開發

2020-05-06 09:10:46

AQS同步器CAS

2023-09-04 13:14:00

裝飾器設計模式

2021-02-26 13:08:27

Java高并發AQS

2014-03-14 10:34:28

JavaJava并發

2021-05-18 06:55:07

Java AQS源碼

2021-06-22 15:27:13

設計模式迭代器模式Java
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美精品一区在线发布 | 国产区在线免费观看 | 麻豆a级片 | 精品av久久久久电影 | 国产精品久久久久久福利一牛影视 | 亚洲国产小视频 | 欧美成ee人免费视频 | 久久国产传媒 | 美人の美乳で授乳プレイ | 日韩精品国产精品 | 欧美日韩亚洲视频 | 国产精品69久久久久水密桃 | 羞羞视频在线网站观看 | 中文字幕第十页 | 国产精品色 | 久久丝袜视频 | 18成人在线观看 | 黄页网址在线观看 | 国产精品国产三级国产aⅴ原创 | 亚洲高清在线 | 久久99精品视频 | 日韩一级| 久久夜色精品国产 | 最新免费视频 | 91免费视频 | 成人免费观看男女羞羞视频 | 久久高清亚洲 | 久久精品亚洲精品国产欧美 | 日本一区二区三区在线观看 | 久久国产精品久久 | 欧美一区二区三区视频在线播放 | 在线观看黄色 | 超碰520| 日日综合 | 日韩a v在线免费观看 | 2021天天躁夜夜看 | 日韩成人精品在线观看 | 久久久久久免费观看 | 精品九九 | 亚洲天天干 | 91麻豆精品国产91久久久更新资源速度超快 |