執行緒基礎(二十一)-併發容器-ArrayBlockingQueue(上)
本文作者:王一飛,叩丁狼高階講師。原創文章,轉載請註明出處。
在正式講解ArrayBlockingQueue類前,先來科普一下執行緒中各類鎖,只有瞭解這些鎖之後,理解ArrayBlockingQueue那就更輕鬆了。
可重入鎖
一種遞迴無阻塞的同步機制,也叫做遞迴鎖。簡單講一個執行緒獲取到鎖物件之後,還是可以再次獲取該鎖物件時,不會發生阻塞。
java中 synchronized 跟ReentrantLock 都是可重入鎖, synchronized 為隱性, 而ReentrantLock 為顯示。 下面以synchronized 為例:
public class ThreadDemo {
public synchronized void method1(){
System.out.println(Thread.currentThread().getName() + "進入method1....");
try {
Thread.sleep(1000);
method2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void method2(){
System.out.println(Thread.currentThread().getName() + "進入method2....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class App {
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
new Thread(new Runnable() {
@Override
public void run() {
threadDemo.method1();
}
}, "t1").start();
new Thread(new Runnable() {
@Override
public void run() {
threadDemo.method2();
}
}, "t2").start();
new Thread(new Runnable() {
@Override
public void run() {
threadDemo.method2();
}
}, "t3").start();
}
}
上面程式碼, t1執行緒先進入method1, 1s之後進入method2, 因為使用synchronized 加鎖,t2, t3執行緒無法進入method2,必須等,而t1執行緒執行完method1後,可以直接進入method2, 無需要重複獲取鎖的操作。
改進: 可以再多開幾個執行緒訪問method2方法,會發現結果是一致的。
不可重入鎖
不可重入鎖是跟重入鎖是對立的,表示一執行緒獲取到鎖物件後,想再次獲取該鎖物件時,必須先釋放之前獲取鎖物件,否則阻塞等待。
java中沒有執行緒類實現不可重入鎖,更多時候,需要我們程式設計實現。
//自定義鎖物件模擬不可重入鎖
public class MyLock {
private boolean isLock = false;
//模擬獲取鎖
public synchronized void lock() throws InterruptedException {
//自旋排除一些硬體執行干擾
while(isLock){
wait();
}
isLock = true;
}
//模擬釋放鎖
public synchronized void unLock(){
isLock = false;
notify();
}
}
public class ThreadDemo {
private MyLock lock = new MyLock();
public void method1() throws InterruptedException {
lock.lock();
System.out.println("method1...in");
method2();
System.out.println("method1...out");
lock.unLock();
}
public void method2() throws InterruptedException {
lock.lock();
System.out.println("method2......");
lock.unLock();
}
}
public class App {
public static void main(String[] args) throws InterruptedException {
ThreadDemo demo = new ThreadDemo();
demo.method1();
}
}
執行之後, 列印只有method1...in, 在執行method1時,呼叫lock.lock()方法, isLock標記量改為true,表示鎖被持有,跳過迴圈。 執行method2時,再次執行lock.lock(),isLock標籤為true, 進入迴圈,執行緒等待。模擬拉當鎖已經被持有,同一個執行緒第二次申請同一把鎖,需要等待。
互斥鎖
同一個時刻,只允許獲取到所物件的執行緒執行。synchronized ReentrantLock 本身就是互斥鎖。
public class ThreadDemo {
//同一個時刻只允許一個執行緒進入
public synchronized void method1(){
System.out.println(Thread.currentThread().getName() + "進入....");
}
}
public class ThreadDemo {
private ReentrantLock lock = new ReentrantLock();
//同一個時刻只允許一個執行緒進入
public void method1(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "進入....");
}finally {
lock.unlock();
}
}
}
自旋鎖
一種非阻塞鎖,也就是說,如果某執行緒需要獲取鎖,但鎖已經被執行緒佔用時,執行緒不阻塞等待,而是通過空迴圈來消耗CPU時間片,等待其他執行緒釋放鎖。注意,自旋鎖中的迴圈也不是瞎迴圈, 一般會設定一定迴圈次數或者迴圈跳出條件。
自旋鎖運用非常廣泛, jdk中的juc包原子操作類中都是, 比如: AtomicInteger
public class AtomicInteger extends Number implements java.io.Serializable {
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
}
Unsafe類
public final int getAndSetInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var4));
return var5;
}
偏向鎖 / 輕量級鎖 / 重量級鎖
偏向鎖,輕量鎖,重量鎖並不是對執行緒加鎖機制,而是jdk1.6之後提出的對執行緒加鎖的優化策略。
偏向鎖:當執行緒沒有競爭的環境下,需要重複獲取某個鎖物件時,jvm為減少開銷,讓執行緒進入偏向模式,再次獲取鎖物件時,取消之前已經獲取鎖同步操作(即一系列的cas判斷),直接取得鎖物件。如果期間有其他執行緒參與競爭,則退出偏向模式。
當執行緒退出偏向模式最後,進入輕量級鎖模式。此時,執行緒嘗試使用自旋方式來獲取鎖,如果獲取成功,繼續邏輯執行, 如果獲取失敗,表示當前鎖物件存在競爭,鎖就會膨脹成重量級鎖。
當執行緒進入重量級鎖模式, 所有操作就跟我們所認知那樣,爭奪CUP,在操作過程中,爭奪失敗執行緒會被作業系統掛起,阻塞等待。那麼執行緒間的切換和呼叫成本就會大大提高,效能也就對應下降了。
樂觀鎖
顧名思義,就是很樂觀,在獲取資料時認為別人不會對資料做修改,所以不上鎖,但是在更新的時候會先判斷別人有沒有更新了此資料,最通用的實現是使用版本號判斷方式,java的juc併發包中的原子操作類使用的CAS機制,其實也是一種樂觀鎖實現。
悲觀鎖
與樂觀鎖是相對的,操作前都假設最壞的情況,在獲取資料的認為別人會對資料做修改,所以每次操作前都會上鎖。而別人想操作此資料就會阻塞直到它拿到鎖。Java中synchronized關鍵字ReentrantLock的實現便是悲觀鎖。
公平鎖
公平鎖,講究公平,當鎖物件被佔用時,參與鎖物件爭奪的執行緒按照FIFO的順序排序等待鎖釋放,人人有機會,不爭不搶。
public class Resource implements Runnable {
private ReentrantLock lock = new ReentrantLock(true); //公平鎖
public void run() {
System.out.println(Thread.currentThread().getName() + " 進入了.....");
lock.lock(); //爭鎖
try {
System.out.println(Thread.currentThread().getName() + " 獲取鎖並執行了.....");
}finally {
lock.unlock();
}
}
}
public class App {
public static void main(String[] args) throws InterruptedException {
Resource resource = new Resource(); //共享資源
for (int i = 0; i <10; i++) {
new Thread(resource, "t_" + i).start();
}
}
}
t_4 進入了.....
t_2 進入了.....
t_4 獲取鎖並執行了.....
t_7 進入了.....
t_6 進入了.....
t_3 進入了.....
t_5 進入了.....
t_2 獲取鎖並執行了.....
t_7 獲取鎖並執行了.....
t_6 獲取鎖並執行了.....
t_3 獲取鎖並執行了.....
t_5 獲取鎖並執行了.....
t_0 進入了.....
t_0 獲取鎖並執行了.....
t_1 進入了.....
t_1 獲取鎖並執行了.....
t_8 進入了.....
t_8 獲取鎖並執行了.....
t_9 進入了.....
t_9 獲取鎖並執行了.....
觀察執行結果,當鎖是公平鎖時(new ReentrantLock(true))會發現進入順序是4,2,7,6,3,5,0,1,8,9 而執行的順序是4,2,7,6,3,5,0,1,8,9。兩者順序一樣,這就是公平的體現,誰先來,誰先執行。
非公平鎖
與公平鎖相對,當鎖物件被釋放時,所有參與爭奪鎖物件的執行緒各憑本事,撐死膽大的,餓死膽小的。
其他程式碼不變,僅僅將引數改為false或者去掉
private ReentrantLock lock = new ReentrantLock(false); //非公平鎖
t_4 進入了.....
t_2 進入了.....
t_5 進入了.....
t_3 進入了.....
t_4 獲取鎖並執行了.....
t_7 進入了.....
t_7 獲取鎖並執行了.....
t_0 進入了.....
t_1 進入了.....
t_0 獲取鎖並執行了.....
t_8 進入了.....
t_2 獲取鎖並執行了.....
t_9 進入了.....
t_9 獲取鎖並執行了.....
t_6 進入了.....
t_5 獲取鎖並執行了.....
t_3 獲取鎖並執行了.....
t_1 獲取鎖並執行了.....
t_8 獲取鎖並執行了.....
t_6 獲取鎖並執行了.....
當鎖是非公平鎖時(new ReentrantLock(false))進入的順序與執行順序不一樣啦,這就是非公平鎖,爭奪CPU各憑本事。
好的,到這執行緒中的鎖知識普及就結束了。
想獲取更多技術乾貨,請前往叩丁狼官網:http://www.wolfcode.cn/all_article.html
相關文章
- python基礎執行緒-管理併發執行緒Python執行緒
- 併發與多執行緒基礎執行緒
- JAVA多執行緒和併發基礎Java執行緒
- Java併發基礎(2)------執行緒池Java執行緒
- 【多執行緒與高併發】- 執行緒基礎與狀態執行緒
- 併發程式設計之多執行緒基礎程式設計執行緒
- Java併發程式設計-執行緒基礎Java程式設計執行緒
- Java併發指南1:併發基礎與Java多執行緒Java執行緒
- Java多執行緒與併發基礎面試題Java執行緒面試題
- iOS開發基礎——執行緒安全(執行緒鎖)iOS執行緒
- C#多執行緒開發-執行緒基礎 01C#執行緒
- 執行緒基礎執行緒
- Java併發基礎04:執行緒技術之死鎖問題Java執行緒
- 66.QT-執行緒併發、QTcpServer併發、QThreadPool執行緒池QT執行緒TCPServerthread
- 程式執行緒篇——程式執行緒基礎執行緒
- Java執行緒池一:執行緒基礎Java執行緒
- 多執行緒併發篇——如何停止執行緒執行緒
- Java併發基礎03:傳統執行緒的互斥技術—synchronizedJava執行緒synchronized
- Java併發基礎06. 執行緒範圍內共享資料Java執行緒
- 併發基礎-第01章-實現執行緒的正確方式執行緒
- 多執行緒基礎執行緒
- Java 執行緒基礎Java執行緒
- Java併發基礎01:揭祕傳統執行緒技術中建立執行緒的兩種方式Java執行緒
- Java 多執行緒基礎(四)執行緒安全Java執行緒
- 多執行緒系列(1),多執行緒基礎執行緒
- 多執行緒系列(三):執行緒池基礎執行緒
- Java併發(四)----執行緒執行原理Java執行緒
- Java併發(一)----程式、執行緒、並行、併發Java執行緒並行
- 併發程式設計之多執行緒執行緒安全程式設計執行緒
- 多執行緒與高併發(二)執行緒安全執行緒
- 併發與多執行緒之執行緒安全篇執行緒
- JAVA多執行緒併發Java執行緒
- 併發容器之ArrayBlockingQueue和LinkedBlockingQueue實現原理詳解BloC
- Java 併發:執行緒、執行緒池和執行器全面教程Java執行緒
- 多執行緒基礎-基礎實現執行緒
- 多執行緒學習一(多執行緒基礎)執行緒
- Java 多執行緒基礎(八)執行緒讓步Java執行緒
- pthread 多執行緒基礎thread執行緒