6.什麼是坎卡倫西?
並發是 Java 中的一個類別庫,其中包含針對跨多個執行緒工作而最佳化的特殊類別。這些類別被收集在一個包中java.util.concurrent
。它們可以根據功能進行如下示意性劃分: 並發集合- 一組在多線程環境中比java.util
包中的標準通用集合更有效地工作的集合。不是Collections.synchronizedList
使用阻塞存取整個集合的基本包裝器,而是使用資料段上的鎖,或使用無等待演算法對資料的平行讀取進行最佳化。 隊列- 具有多線程支援的非阻塞和阻塞隊列。非阻塞隊列旨在提高速度並在不阻塞線程的情況下進行操作。當某些條件不滿足時,例如隊列為空或溢出,或沒有空閒的“消費者”,您需要“減慢”“生產者”或“消費者”線程的速度時,可以使用阻塞隊列。 同步器是用於同步執行緒的輔助實用程式。它們是「並行」計算中的強大武器。 Executors - 包含用於建立執行緒池、調度非同步任務和獲取結果的優秀框架。 鎖- 代表與基本同步機制 synchronized
相比的替代且更靈活的線程同步機制。原子- 支援對基元和引用進行原子操作的類別。 來源:wait
notify
notifyAll
7. 你知道 Kankarensi 的哪些課程?
這個問題的答案在這篇文章中得到了完美的闡述。我不認為在這裡重印所有內容有什麼意義,因此我將只描述那些我有幸簡要地熟悉的類別。 ConcurrentHashMap<K, V> -與 和上的Hashtable
區塊不同,資料以段的形式呈現,分為鍵的雜湊值。因此,資料是按段落存取的,而不是按單一物件存取的。另外,迭代器代表特定時間段的數據,不會拋出異常。 AtomicBoolean、AtomicInteger、AtomicLong、AtomicIntegerArray、AtomicLongArray - 如果在類別中您需要同步存取簡單類型變數怎麼辦?您可以將構造與, 一起使用,並且在使用原子操作,時使用。但是您可以透過使用新類別來做得更好。由於使用了 CAS,這些類的操作比通過. 另外,還有依照給定數量進行原子加法以及遞增/遞減的方法。 synhronized
HashMap
ConcurrentModificationException
int
synchronized
set/get
volatile
Atomic*
synchronized/volatile
8. ConcurrentHashMap 類別如何運作?
在其引入時,ConcurrentHashMap
Java 開發人員需要以下雜湊映射實作:
- 線程安全
- 訪問整個表時沒有鎖
- 執行讀取操作時最好沒有表鎖
ConcurrentHashMap
如下:
-
地圖元素
與元素不同
HashMap
,Entry
inConcurrentHashMap
被聲明為volatile
。這是一個重要的特性,也是由於JMM的變化所致。static final class HashEntry<K, V> { final K key; final int hash; volatile V value; final HashEntry<K, V> next; HashEntry(K key, int hash, HashEntry<K, V> next, V value) { this .key = key; this .hash = hash; this .next = next; this .value = value; } @SuppressWarnings("unchecked") static final <K, V> HashEntry<K, V>[] newArray(int i) { return new HashEntry[i]; } }
-
哈希函數
ConcurrentHashMap
也使用了改進的雜湊函數。HashMap
讓我提醒一下JDK 1.2中的情況:static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
ConcurrentHashMap JDK 1.5 版本:
private static int hash(int h) { h += (h << 15) ^ 0xffffcd7d; h ^= (h >>> 10); h += (h << 3); h ^= (h >>> 6); h += (h << 2) + (h << 14); return h ^ (h >>> 16); }
為什麼需要使雜湊函數變得更複雜?哈希映射中的表的長度由 2 的冪決定。對於二進位表示在低位和高位上沒有區別的雜湊碼,我們將會發生衝突。增加雜湊函數的複雜度正好解決了這個問題,減少了映射中發生衝突的可能性。
-
段
此映射被分成N個不同的段(預設為16個,最大值可以是16位元且是2的冪)。每個段都是一個線程安全的映射元素表。增加段的數量將鼓勵修改操作跨越多個段,從而降低運行時阻塞的可能性。
-
並發級別
此參數會影響記憶卡的使用情況和卡片中的段數。
段的數量將被選擇為大於 concurrencyLevel 的最接近的 2 的冪。降低 concurrencyLevel 會使執行緒在寫入時更有可能阻塞映射段。高估該指標會導致記憶體使用效率低。如果只有一個執行緒會修改map,其餘執行緒都會讀取,建議使用值1。
-
全部的
那麼,主要優點和實現特點
ConcurrentHashMap
:- 地圖有一個類似的
hashmap
互動介面 - 讀取操作不需要鎖定並且並行執行
- 寫入操作通常也可以並行執行而不會阻塞
- 創建時註明需要的
concurrencyLevel
,透過讀寫統計來確定 - 地圖元素的值
value
聲明為volatile
- 地圖有一個類似的
9. 什麼是Lock類別?
為了控制對共享資源的訪問,我們可以使用鎖作為同步運算符的替代方案。鎖定功能封裝在java.util.concurrent.locks
. 首先,執行緒嘗試存取共享資源。如果它是空閒的,則在該線程上放置一個鎖。一旦工作完成,共享資源上的鎖就會被釋放。如果資源未釋放並且已在其上放置了鎖,則執行緒將等待直到該鎖被釋放。鎖類別實作一個Lock
定義以下方法的介面:
void lock():
等待直到取得鎖boolean tryLock():
嘗試取得鎖;如果取得鎖,則傳回true。如果未取得鎖,則傳回false。與該方法不同的是,lock()
如果鎖不可用,它不會等待獲取鎖void unlock():
移除鎖Condition newCondition():
Condition
傳回與目前鎖關聯的對象
lock()
,在完成共用資源的操作後,呼叫該方法unlock()
,從而釋放鎖定。該物件Condition
允許您管理阻塞。通常,要使用鎖,需要使用ReentrantLock
包中的一個類別。java.util.concurrent.locks.
該類別實作了 介面Lock
。讓我們以一個小程式為例來看看如何使用 Java Lock API: 那麼,假設我們有一個類,它Resource
有幾個線程安全的方法和不需要線程安全的方法。
public class Resource {
public void doSomething(){
// пусть здесь происходит работа с базой данных
}
public void doLogging(){
// потокобезопасность для логгирования нам не требуется
}
}
現在讓我們來看一個實作介面Runnable
並使用類別方法的類別Resource
。
public class SynchronizedLockExample implements Runnable{
// экземпляр класса Resource для работы с методами
private Resource resource;
public SynchronizedLockExample(Resource r){
this.resource = r;
}
@Override
public void run() {
synchronized (resource) {
resource.doSomething();
}
resource.doLogging();
}
}
現在讓我們使用 Lock API 而不是synchronized
.
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// класс для работы с Lock API. Переписан с приведенной выше программы,
// но уже без использования ключевого слова synchronized
public class ConcurrencyLockExample implements Runnable{
private Resource resource;
private Lock lock;
public ConcurrencyLockExample(Resource r){
this.resource = r;
this.lock = new ReentrantLock();
}
@Override
public void run() {
try {
// лочим на 10 секунд
if(lock.tryLock(10, TimeUnit.SECONDS)){
resource.doSomething();
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
//убираем лок
lock.unlock();
}
// Для логгирования не требуется потокобезопасность
resource.doLogging();
}
}
從程式中可以看到,我們使用該方法tryLock()
來確保執行緒只等待一定的時間。如果它沒有獲得物件的鎖,它只會記錄並退出。另一個重要的一點。必須使用塊try-finally
來確保即使方法doSomething()
拋出異常也能釋放鎖。 資料來源:
11.什麼是互斥體?
互斥體是用於同步執行緒/程序的特殊物件。它可以有兩種狀態──忙碌和空閒。簡單來說,互斥體是一個布林變量,它有兩個值:busy (true) 和 free (false)。當執行緒想要物件的獨佔所有權時,它將其互斥體標記為忙碌,當它完成對物件的處理時,它將其互斥標記為空閒。Java 中的每個物件都附加了一個互斥體。只有 Java 機器可以直接存取互斥鎖。它對程式設計師是隱藏的。12.什麼是顯示器?
監視器是一種特殊的機制(一段代碼) - 互斥鎖上的附加元件,可確保其正確操作。畢竟,僅僅標記物件忙碌是不夠的;我們還必須確保其他線程不會嘗試使用忙碌物件。在Java中,監視器是使用關鍵字來實現的synchronized
。當我們編寫同步區塊時,Java編譯器將其替換為三段程式碼:
- 在區塊的開頭
synchronized
,加入了將互斥體標記為忙碌的程式碼。 - 在區塊的末尾,
synchronized
添加了一個程式碼,將互斥鎖標記為空閒。 - 在該區塊之前,
synchronized
加入程式碼來檢查互斥鎖是否繁忙,然後執行緒必須等待它被釋放。
GO TO FULL VERSION