【多執行緒與高併發3】常用鎖例項
各式鎖的實際應用
樂觀鎖 cas(要改的物件,期望的值,要給的值)無鎖操作,其實是一個樂觀鎖…cas本身可以看成是一個鎖
- automic : 一種使用 cas 實現的原子性操作
原子操作的簡單方法:
函式 | 效果 | 備註 |
---|---|---|
AtomicInteger a = new AtomicInteger(0); | int a = 0; | 建立物件a並且賦初值為0; |
a.incrementAndGet( ); | i++; | 對原值+1後返回; |
a.getAndIncrement( ); | ++i; | 對原值返回後+1; |
a.addAndGet(i); | a+=i; | 返回a+i; |
a.getAndAdd(i); | a+=i; | 返回原值之後給a+i; |
線上程很多的情況下:LongAdder(分段鎖:線上程多的時候有優勢) > Atomic > synchronized。
Synchronized 的可重入性:
//可重入: synchronized void m1(){ for(int i = 1;i<10;i++){ try{ TimeUtil.SECONDS.sleep(1);// 睡一秒 }catch(InterruptedException e){ e.printStackTrace(); } sout(i); } } synchronized void m2(){sout("m2...");} public static void main(String[] args){ T01_ReentrantLock1 r1 = new T01_ReentrantLock1(); new Thread(r1::m1).start(); try{ TimeUtil.SECONDS.sleep(1);// 睡一秒 }catch(InterruptedException e){ e.printStackTrace(); } new Thread(r1::m2).start(); }
輸出結果:0 1 23 4 5 6 7 8 9 m2…
程式碼修改:synchronized
//可重入: synchronized void m1(){ for(int i = 1;i<10;i++){ try{ TimeUtil.SECONDS.sleep(1);// 睡一秒 }catch(InterruptedException e){ e.printStackTrace(); } sout(i); if(i == 2){ new Thread(r1::m2).start(); } } } synchronized void m2(){sout("m2...");} public static void main(String[] args){ T01_ReentrantLock1 r1 = new T01_ReentrantLock1(); new Thread(r1::m1).start(); try{ TimeUtil.SECONDS.sleep(1);// 睡一秒 }catch(InterruptedException e){ e.printStackTrace(); } }
輸出結果:0 1 2 m2 … 3 4 5 6 7 8 9
lock()
:替代 synchronized 的方法;
Lock lock = new ReentrantLock();
-
特點:
- 需要手動上鎖
lock.lock( );
- 需要手動解鎖
lock.unlock( )
; - 防止程式出錯而導致死鎖,需要
try{ …… }catch( ){ …… }
- 需要手動上鎖
-
優點:
-
可以使用
tryLock()
嘗試上鎖; -
當
synchronized
遇到鎖之後只能等待,而tryLock()
可以自定義等待時間; -
locked = lock.tryLock(SECONDS(時間長度),TimeUtil.SECONDS(時間格式:秒));
-
-
常用方法:
方法 | 引數 | 用法 |
---|---|---|
.lock( ); | null | 鎖定 |
.unlock( ); | null | 釋放 |
.tryLock(n,TimeUtil.SECONDS); | 時間長度 時間單位 | 等待引數 時間過程中:如果當前程式釋放了,則鎖定; 不釋放則不鎖定; |
.lockInterruptibly( ); | null; | 可以相應被打斷的鎖; |
.interrupt( ); | Null; | 打斷這個鎖; |
公平鎖
ReetrantLock lock = new ReentrantLock( true );
- 概念:
- 當執行佇列中有執行緒正在排隊的時候:
- 公平鎖:繼續等待,排隊執行;
- 不公平鎖:不等待,直接搶,有可能搶到第一個執行;
- 當執行佇列中有執行緒正在排隊的時候:
- 建立方式:
- 在建立鎖的時候加個
true
建立出來的就是公平鎖;
- 在建立鎖的時候加個
public class T05_ReentrantLock extends Thread(){
private stratic ReentrantLock lock = new ReentrantLock(true);
public void run(){
for(int i = 0;i<100;i++){
lock.lock();
try{
Sout(Thread.currentThread().getName()+"獲得鎖");
}finally{
lock.unlock();
}
}
}
}
一個倒數計時的門栓 CountDownLatch
CountDownLatch latch = CountDownLatch( threads.length );
//建立一個length長度的門栓
.await()
阻塞
原join()
當前執行緒結束自動往前走
.countDown()
原子性–
柵欄工具 CyclicBarrier
迴圈柵欄工具
// 一個引數:不到20的時候,等待,到了20個,這20個發車,再來的繼續等待
CyclicBarrier barrier = new CyclicBarrier(20);
// 兩個引數:
CyclicBarrier barrier = new CyclicBarrier(20,run);
run(){ Sout("滿員,發車!"); }
//lambdo 表示式
CyclicBarrier barrier = new CyclicBarrier(20,()->Sout("滿員,發車!"));
同步進行的 Phaser
按照不同的階段對執行緒進行劃分。
-
使用場景:
- 遺傳演算法
- 現實生活一步一步執行的場景(如:婚禮)
- 像是一個一個柵欄一樣
-
使用方法:
-
自定義一個類,繼承
Phaser
類;static class MarrigePhaser extends Phaser
-
重寫
onAdvance
方法;(柵欄被推倒的時候自動呼叫)protected boolean onAdvance(int phase,int registeredParties)
-
-
方法:
phaser.arriveAndAwaitAdvance(); //執行結束,開始等待; phaser.arriveAndDeregister(); //執行結束,不進入下一階段;
讀寫鎖
程式中的讀寫鎖(一種排他鎖、共享鎖)
-
概念
- 當
A
程式在讀取ABCD
的時候,B
程式也來讀取ABCD
,同時發現A
程式在讀取,則讀取成功; - 當
A
程式在讀取ABCD
的時候,B
程式來修改ABCD
,同時發現A
程式在讀取,若此時更改ABCD
的內容,則A
程式讀取會出問題,所以修改失敗; - **總結:**兩個都是讀取的程式可以同時進行,當有 讀 程式在進行時,無法進行 寫 程式,寫同理;
- 當
-
作用
- 避免 / 減少 髒資料
static ReadWriteLoak readWriteLock = new ReentrantReadWriteLock();
//在 ReentrantReadWriteLock 中 分出一個 `readLock`一個`writeLock`
static Lock readLock = readWriteLock.readLock();
static Lock writeLock = readWriteLock.writeLock();
public static void read(Lock lock){
try{
lock.lock();
Thread.sleep(1000);
Sout("read over!");
// 模擬讀取過程
}catch(InterruptedException e){
e.peintStackTrace();
}finally{
lock.unlock();
}
}
public static void write(Lock lock,int a){
try{
lock.lock();
Thread.sleep(1000);
Sout("write "+ a +"over!");
// 模擬讀取過程
}catch(InterruptedException e){
e.peintStackTrace();
}finally{
lock.unlock();
}
}
public static void main(String[] args){
Runnable readR = ()->read(lock);
//Runnable readR = ()->read(readLock);
Runnable write = ()->write(lock,new Random().nextInt());
for (int i=0;i<18;i++)new Thread(readR ).start();
for (int i=0;i<2 ;i++)new Thread(writeR).start();
}
}
// 如果使用 ReentrantLock
的話,以上程式碼在執行讀
的時候也需要等待一秒;
// 解決方法:將Main
方法中的讀
鎖換成Runnable readR = ()-> read(readLock);
Semaphore 一個有意思的執行緒池
Semaphore s = new Semaphore(x);
x是幾則這個 < 執行緒池 > 就 允許幾個執行緒 同時執行。
public static void main(String[] args){
Semaphore s = new Semaphore(1);
//括號中數字為x時,允許x個執行緒同時執行
// T1 Running
new Thread(()->{
try{
s.acquire();
// 進來一個程式 1 變成 0 ,別的執行緒不能執行
Sout("T1 Running");
Thread.sleep(200);
Sout("T1 Running");
}catch(InterruptedException e){
e.printStackTrace();
}finally{
s.release();
// 離開一個程式 0 變成 1 ,別的執行緒可以執行
}
});
// T2 Running
new Thread(()->{
try{
s.acquire();
// 進來一個程式 1 變成 0 ,別的執行緒不能執行
Sout("T2 Running");
Thread.sleep(200);
Sout("T2 Running");
}catch(InterruptedException e){
e.printStackTrace();
}finally{
s.release();
// 離開一個程式 0 變成 1 ,別的執行緒可以執行
}
});
}
如果x==1
則執行結果是T1 T1 T2 T2
,否則可能是T1 T2 T1 T2
Exchanger 用於 < ! 兩個 ! > 執行緒交換資料的方法
使用場景:雙人遊戲中兩人交換裝備!執行一次就失效,可以迴圈等待下一次;
public static void main(String[] args){
// T1
new Thread(()->{
String s = "T1";
try{
s = sxchanger.exchange(s);
}cathc(InterruptedException e){
e.printStackTrace();
}
Sout(Thread.currentThread().getName()+""+s);
},"t1").start();
// T2
new Thread(()->{
String s = "T2";
try{
s = sxchanger.exchange(s);
}cathc(InterruptedException e){
e.printStackTrace();
}
Sout(Thread.currentThread().getName()+""+s);
},"t2").start();
}
執行緒中有兩個變數,分別是
s
和s
(區域性變數),兩個執行緒同時執行,最後交換T1
與T2
的值;
分散式鎖
只是某個型別的鎖,將來補充概念。
總結 :
- 無論何種情況,優先考慮使用
synchronized
- 讀寫的時候
讀寫鎖
可以大大提升效率 lock
比synchronized
要靈活很多,但是需要自己加鎖並釋放鎖,所以不是很方便
相關文章
- 多執行緒併發鎖分類以及簡單例項執行緒單例
- 多執行緒與高併發(二)執行緒安全執行緒
- 多執行緒與高併發(一)多執行緒入門執行緒
- 多執行緒與併發----讀寫鎖執行緒
- 【多執行緒與高併發】- 執行緒基礎與狀態執行緒
- 多執行緒與併發-----Lock鎖技術執行緒
- Java多執行緒/併發06、執行緒鎖Lock與ReadWriteLockJava執行緒
- Java高併發與多執行緒(一)-----概念Java執行緒
- 【多執行緒與高併發 2】volatile 篇執行緒
- 【多執行緒與高併發】- 淺談volatile執行緒
- JUC之Exchanger-多執行緒與高併發執行緒
- 分散式叢集與多執行緒高併發分散式執行緒
- Java高併發與多執行緒(二)-----執行緒的實現方式Java執行緒
- 多執行緒與高併發(三)synchronized關鍵字執行緒synchronized
- 併發與多執行緒之執行緒安全篇執行緒
- 【多執行緒與高併發】從一則招聘資訊進入多執行緒的世界執行緒
- 多執行緒併發程式設計“鎖”事執行緒程式設計
- 併發與多執行緒基礎執行緒
- 多執行緒與併發----Semaphere同步執行緒
- java多執行緒與併發 - 併發工具類Java執行緒
- Java多執行緒/併發05、synchronized應用例項:執行緒間操作共享資料Java執行緒synchronized
- java多執行緒例項Java執行緒
- java多執行緒與併發 - 執行緒池詳解Java執行緒
- 多執行緒與高併發(五)final關鍵字執行緒
- 分散式、高併發與多執行緒有何區別分散式執行緒
- 【多執行緒與高併發原理篇:3_java記憶體模型】執行緒Java記憶體模型
- 多執行緒高併發解決辦法執行緒
- Java高併發與多執行緒(三)-----執行緒的基本屬性和主要方法Java執行緒
- Qt中的多執行緒與執行緒池淺析+例項QT執行緒
- Java多執行緒/併發10、不可重入鎖/自旋鎖、可重入鎖Java執行緒
- 你分得清分散式、高併發與多執行緒嗎?分散式執行緒
- 分散式、高併發與多執行緒、你分辨的清嗎?分散式執行緒
- JAVA多執行緒併發Java執行緒
- 23、Java併發性和多執行緒-重入鎖死Java執行緒
- 多執行緒併發篇——如何停止執行緒執行緒
- 【多執行緒與高併發】Java守護執行緒是什麼?什麼是Java的守護執行緒?執行緒Java
- 多執行緒併發:以AQS中acquire()方法為例來分析多執行緒間的同步與協作執行緒AQSUI
- Java多執行緒與併發之ThreadLocalJava執行緒thread