1. 程式人生 > 其它 >JUC程式設計(一)-Lock鎖,Condition

JUC程式設計(一)-Lock鎖,Condition

一.什麼是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();
        }
    }
}