JUC程式設計(一)-Lock鎖,Condition
阿新 • • 發佈:2021-10-08
一.什麼是JUC
JUC是java.util.concurrent的縮寫。一共有三個相關的jar包。高併發程式設計工具包。
併發程式設計本質:充分利用CPU資源。
補充:
- java不能開啟執行緒,start()實際上是呼叫本地方法start0(),底層的c++方法去開啟執行緒。
- wait與sleep區別:1.wait屬於Object類,sleep屬於Thread類。2.sleep不釋放鎖,wait釋放鎖。3.wait依賴於synchronized。4.wait需要被喚醒,sleep不需要。
二.Lock鎖
- Lock是一個介面,有三個實現類:
- ReentrantLock (預設是非公平鎖,可以插隊;可以手動設定為公平鎖)
- ReentrantReadWriteLock.ReadLock
- ReentrantReadWriteLock.WriteLock
- 對共享資源獨佔訪問:一次只能有一個執行緒可以獲取鎖,並且對共享資源的所有訪問都要求首先獲取鎖。
- 與synchronized區別:
-
- lock可以判斷鎖的狀態,synchronized不能
- lock必須手動釋放鎖,synchronized自動釋放鎖
- synchronized與lock均可重入鎖(指某個執行緒已經獲得某個鎖,可以再次獲取鎖而不會出現死鎖) ,均為非公平鎖。
- synchronized,一個執行緒獲得鎖,另一個執行緒會一直等待直到其釋放鎖;lock則不會,可通過lock.tryLock()嘗試獲取鎖。
- 常用用法:
Lock lock = new ReentrantLock(); public void method(){ lock.lock(); try { //同步程式碼塊 } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }
三.生產者消費者問題
synchronized實現:
點選檢視程式碼
public class ProCon { public static void main(String[] args) { Number number = new Number(); new Thread(()->{ for (int i = 0; i < 5; i++) { try { number.add(); } catch (InterruptedException e) { e.printStackTrace(); } } },"a").start(); new Thread(()->{ for (int i = 0; i < 5; i++) { try { number.sub(); } catch (InterruptedException e) { e.printStackTrace(); } } },"b").start(); new Thread(()->{ for (int i = 0; i < 5; i++) { try { number.add(); } catch (InterruptedException e) { e.printStackTrace(); } } },"c").start(); new Thread(()->{ for (int i = 0; i < 5; i++) { try { number.sub(); } catch (InterruptedException e) { e.printStackTrace(); } } },"d").start(); } } /* 等待,業務處理,通知 */ class Number{ private int num = 0; public synchronized void add() throws InterruptedException { while (num!=0){ this.wait(); } num++; // num等於0才加1,即num++最大隻能為1 this.notifyAll(); System.out.println(Thread.currentThread().getName()+"->"+num); } public synchronized void sub() throws InterruptedException { while (num==0){ this.wait(); } num--; this.notifyAll(); System.out.println(Thread.currentThread().getName()+"->"+num); } }
注:該方法中同步程式碼塊中使用if判斷等待存在虛假喚醒的可能性,使用while判斷可規避該狀況。
Lock鎖實現(JUC):
Lock替換synchronized方法和語句的使用,Condition取代了物件監視器Obj。Condition提供與物件監視器類似的方法作用,即await(),signal(),signalAll()對應於wait(),notify(),notifyAll()。 一個Condition例項本質上繫結到一個鎖。 要獲得特定Condition例項的Condition例項,使用newCondition方法。點選檢視程式碼
public class ProCon2 {
public static void main(String[] args) {
Number2 number2 = new Number2();
new Thread(()->{ for (int i = 0; i < 5; i++) { number2.add(); } },"a").start();
new Thread(()->{ for (int i = 0; i < 5; i++) { number2.sub(); } },"b").start();
new Thread(()->{ for (int i = 0; i < 5; i++) { number2.add(); } },"c").start();
new Thread(()->{ for (int i = 0; i < 5; i++) { number2.sub(); } },"d").start();
}
}
class Number2{
private int num = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void add(){
lock.lock();
try {
while (num!=0){
condition.await();
}
num++;
condition.signalAll();
System.out.println(Thread.currentThread().getName()+"->"+num);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void sub(){
lock.lock();
try {
while (num==0){
condition.await();
}
num--;
condition.signalAll();
System.out.println(Thread.currentThread().getName()+"->"+num);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
Condition的優勢:可以精準地喚醒執行緒
點選檢視程式碼
public class ProCon3 {
public static void main(String[] args) {
ThreadTest test = new ThreadTest();
new Thread(()->{ for (int i = 0; i < 5; i++) { test.A(); } },"a").start();
new Thread(()->{ for (int i = 0; i < 5; i++) { test.B(); } },"b").start();
new Thread(()->{ for (int i = 0; i < 5; i++) { test.C(); } },"c").start();
}
}
class ThreadTest{
private int n = 1;
Lock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
public void A(){
lock.lock();
try {
while (n!=1){
conditionA.await();
}
n = 2;
// n為1執行業務,喚醒B
conditionB.signal();
System.out.println(Thread.currentThread().getName()+"->"+"is A");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void B(){
lock.lock();
try {
while (n!=2){
conditionB.await();
}
n = 3;
// n為2執行業務,喚醒C
conditionC.signal();
System.out.println(Thread.currentThread().getName()+"->"+"is B");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void C(){
lock.lock();
try {
while (n!=3){
conditionC.await();
}
n = 1;
// n為3執行業務,喚醒A
conditionA.signal();
System.out.println(Thread.currentThread().getName()+"->"+"is C");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}