程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 詳解Java多線程編程中互斥鎖ReentrantLock類的用法

詳解Java多線程編程中互斥鎖ReentrantLock類的用法

編輯:關於JAVA

詳解Java多線程編程中互斥鎖ReentrantLock類的用法。本站提示廣大學習愛好者:(詳解Java多線程編程中互斥鎖ReentrantLock類的用法)文章只能為提供參考,不一定能成為您想要的結果。以下是詳解Java多線程編程中互斥鎖ReentrantLock類的用法正文


0.關於互斥鎖

所謂互斥鎖, 指的是一次最多只能有一個線程持有的鎖. 在jdk1.5之前, 我們平日應用synchronized機制掌握多個線程對同享資本的拜訪. 而如今, Lock供給了比synchronized機制更普遍的鎖定操作, Lock和synchronized機制的重要差別:
synchronized機制供給了對與每一個對象相干的隱式監督器鎖的拜訪, 並強迫一切鎖獲得和釋放均要湧現在一個塊構造中, 當獲得了多個鎖時, 它們必需以相反的次序釋放. synchronized機制對鎖的釋放是隱式的, 只需線程運轉的代碼超越了synchronized語句塊規模, 鎖就會被釋放. 而Lock機制必需顯式的挪用Lock對象的unlock()辦法能力釋放鎖, 這為獲得鎖和釋放鎖不湧現在統一個塊構造中, 和以更自在的次序釋放鎖供給了能夠。

1. ReentrantLock引見
ReentrantLock是一個可重入的互斥鎖,又被稱為“獨有鎖”。
望文生義,ReentrantLock鎖在統一個時光點只能被一個線程鎖持有;而可重入的意思是,ReentrantLock鎖,可以被單個線程屢次獲得。
ReentrantLock分為“公正鎖”和“非公正鎖”。它們的差別表現在獲得鎖的機制上能否公正。“鎖”是為了掩護競爭資本,避免多個線程同時操作線程而失足,ReentrantLock在統一個時光點只能被一個線程獲得(當某線程獲得到“鎖”時,其它線程就必需期待);ReentraantLock是經由過程一個FIFO的期待隊列來治理獲得該鎖一切線程的。在“公正鎖”的機制下,線程順次列隊獲得鎖;而“非公正鎖”在鎖是可獲得狀況時,不論本身是否是在隊列的開首都邑獲得鎖。

ReentrantLock函數列表

// 創立一個 ReentrantLock ,默許是“非公正鎖”。
ReentrantLock()
// 創立戰略是fair的 ReentrantLock。fair為true表現是公正鎖,fair為false表現長短公正鎖。
ReentrantLock(boolean fair)

// 查詢以後線程堅持此鎖的次數。
int getHoldCount()
// 前往今朝具有此鎖的線程,假如此鎖不被任何線程具有,則前往 null。
protected Thread getOwner()
// 前往一個 collection,它包括能夠正期待獲得此鎖的線程。
protected Collection<Thread> getQueuedThreads()
// 前往正期待獲得此鎖的線程估量數。
int getQueueLength()
// 前往一個 collection,它包括能夠正在期待與此鎖相干給定前提的那些線程。
protected Collection<Thread> getWaitingThreads(Condition condition)
// 前往期待與此鎖相干的給定前提的線程估量數。
int getWaitQueueLength(Condition condition)
// 查詢給定線程能否正在期待獲得此鎖。
boolean hasQueuedThread(Thread thread)
// 查詢能否有些線程正在期待獲得此鎖。
boolean hasQueuedThreads()
// 查詢能否有些線程正在期待與此鎖有關的給定前提。
boolean hasWaiters(Condition condition)
// 假如是“公正鎖”前往true,不然前往false。
boolean isFair()
// 查詢以後線程能否堅持此鎖。
boolean isHeldByCurrentThread()
// 查詢此鎖能否由隨意率性線程堅持。
boolean isLocked()
// 獲得鎖。
void lock()
// 假如以後線程未被中止,則獲得鎖。
void lockInterruptibly()
// 前往用來與此 Lock 實例一路應用的 Condition 實例。
Condition newCondition()
// 僅在挪用時鎖未被另外一個線程堅持的情形下,才獲得該鎖。
boolean tryLock()
// 假如鎖在給定期待時光內沒有被另外一個線程堅持,且以後線程未被中止,則獲得該鎖。
boolean tryLock(long timeout, TimeUnit unit)
// 試圖釋放此鎖。
void unlock()

2. ReentrantLock示例
經由過程比較“示例1”和“示例2”,我們可以或許清楚的熟悉lock和unlock的感化
2.1 示例1

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

// LockTest1.java
// 倉庫
class Depot { 
 private int size;  // 倉庫的現實數目
 private Lock lock;  // 獨有鎖

 public Depot() {
  this.size = 0;
  this.lock = new ReentrantLock();
 }

 public void produce(int val) {
  lock.lock();
  try {
   size += val;
   System.out.printf("%s produce(%d) --> size=%d\n", 
     Thread.currentThread().getName(), val, size);
  } finally {
   lock.unlock();
  }
 }

 public void consume(int val) {
  lock.lock();
  try {
   size -= val;
   System.out.printf("%s consume(%d) <-- size=%d\n", 
     Thread.currentThread().getName(), val, size);
  } finally {
   lock.unlock();
  }
 }
}; 

// 臨盆者
class Producer {
 private Depot depot;

 public Producer(Depot depot) {
  this.depot = depot;
 }

 // 花費產物:新建一個線程向倉庫中臨盆產物。
 public void produce(final int val) {
  new Thread() {
   public void run() {
    depot.produce(val);
   }
  }.start();
 }
}

// 花費者
class Customer {
 private Depot depot;

 public Customer(Depot depot) {
  this.depot = depot;
 }

 // 花費產物:新建一個線程從倉庫中花費產物。
 public void consume(final int val) {
  new Thread() {
   public void run() {
    depot.consume(val);
   }
  }.start();
 }
}

public class LockTest1 { 
 public static void main(String[] args) { 
  Depot mDepot = new Depot();
  Producer mPro = new Producer(mDepot);
  Customer mCus = new Customer(mDepot);

  mPro.produce(60);
  mPro.produce(120);
  mCus.consume(90);
  mCus.consume(150);
  mPro.produce(110);
 }
}

運轉成果:

Thread-0 produce(60) --> size=60
Thread-1 produce(120) --> size=180
Thread-3 consume(150) <-- size=30
Thread-2 consume(90) <-- size=-60
Thread-4 produce(110) --> size=50

成果剖析:
(1) Depot 是個倉庫。經由過程produce()能往倉庫中臨盆貨色,經由過程consume()能花費倉庫中的貨色。經由過程獨有鎖lock完成對倉庫的互斥拜訪:在操作(臨盆/花費)倉庫中貨物前,會先經由過程lock()鎖住倉庫,操作完以後再經由過程unlock()解鎖。
(2) Producer是臨盆者類。挪用Producer中的produce()函數可以新建一個線程往倉庫中臨盆產物。
(3) Customer是花費者類。挪用Customer中的consume()函數可以新建一個線程花費倉庫中的產物。
(4) 在主線程main中,我們會新建1個臨盆者mPro,同時新建1個花費者mCus。它們分離向倉庫中臨盆/花費產物。
依據main中的臨盆/花費數目,倉庫終究殘剩的產物應當是50。運轉成果是相符我們預期的!
這個模子存在兩個成績:
(1) 實際中,倉庫的容量弗成能為正數。然則,此模子中的倉庫容量可認為正數,這與實際相抵觸!
(2) 實際中,倉庫的容量是無限制的。然則,此模子中的容量確切沒無限制的!
這兩個成績,我們略微會講到若何處理。如今,先看個簡略的示例2;經由過程比較“示例1”和“示例2”,我們能更清楚的熟悉lock(),unlock()的用處。

2.2 示例2

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

// LockTest2.java
// 倉庫
class Depot { 
  private int size;    // 倉庫的現實數目
  private Lock lock;    // 獨有鎖

  public Depot() {
    this.size = 0;
    this.lock = new ReentrantLock();
  }

  public void produce(int val) {
//    lock.lock();
//    try {
      size += val;
      System.out.printf("%s produce(%d) --> size=%d\n", 
          Thread.currentThread().getName(), val, size);
//    } catch (InterruptedException e) {
//    } finally {
//      lock.unlock();
//    }
  }

  public void consume(int val) {
//    lock.lock();
//    try {
      size -= val;
      System.out.printf("%s consume(%d) <-- size=%d\n", 
          Thread.currentThread().getName(), val, size);
//    } finally {
//      lock.unlock();
//    }
  }
};

// 臨盆者
class Producer {
  private Depot depot;

  public Producer(Depot depot) {
    this.depot = depot;
  }

  // 花費產物:新建一個線程向倉庫中臨盆產物。
  public void produce(final int val) {
    new Thread() {
      public void run() {
        depot.produce(val);
      }
    }.start();
  }
}

// 花費者
class Customer {
  private Depot depot;

  public Customer(Depot depot) {
    this.depot = depot;
  }

  // 花費產物:新建一個線程從倉庫中花費產物。
  public void consume(final int val) {
    new Thread() {
      public void run() {
        depot.consume(val);
      }
    }.start();
  }
}

public class LockTest2 { 
  public static void main(String[] args) { 
    Depot mDepot = new Depot();
    Producer mPro = new Producer(mDepot);
    Customer mCus = new Customer(mDepot);

    mPro.produce(60);
    mPro.produce(120);
    mCus.consume(90);
    mCus.consume(150);
    mPro.produce(110);
  }
}


(某一次)運轉成果:

Thread-0 produce(60) --> size=-60
Thread-4 produce(110) --> size=50
Thread-2 consume(90) <-- size=-60
Thread-1 produce(120) --> size=-60
Thread-3 consume(150) <-- size=-60

成果解釋:
“示例2”在“示例1”的基本上去失落了lock鎖。在“示例2”中,倉庫中終究殘剩的產物是-60,而不是我們希冀的50。緣由是我們沒有完成對倉庫的互斥拜訪。

2.3 示例3
在“示例3”中,我們經由過程Condition去處理“示例1”中的兩個成績:“倉庫的容量弗成能為正數”和“倉庫的容量是無限制的”。
處理該成績是經由過程Condition。Condition是須要和Lock結合應用的:經由過程Condition中的await()辦法,能讓線程壅塞[相似於wait()];經由過程Condition的signal()辦法,能讓叫醒線程[相似於notify()]。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;

// LockTest3.java
// 倉庫
class Depot {
  private int capacity;  // 倉庫的容量
  private int size;    // 倉庫的現實數目
  private Lock lock;    // 獨有鎖
  private Condition fullCondtion;      // 臨盆前提
  private Condition emptyCondtion;    // 花費前提

  public Depot(int capacity) {
    this.capacity = capacity;
    this.size = 0;
    this.lock = new ReentrantLock();
    this.fullCondtion = lock.newCondition();
    this.emptyCondtion = lock.newCondition();
  }

  public void produce(int val) {
    lock.lock();
    try {
       // left 表現“想要臨盆的數目”(有能夠臨盆量太多,需多今生產)
      int left = val;
      while (left > 0) {
        // 庫存已滿時,期待“花費者”花費產物。
        while (size >= capacity)
          fullCondtion.await();
        // 獲得“現實臨盆的數目”(即庫存中新增的數目)
        // 假如“庫存”+“想要臨盆的數目”>“總的容量”,則“現實增量”=“總的容量”-“以後容量”。(此時填滿倉庫)
        // 不然“現實增量”=“想要臨盆的數目”
        int inc = (size+left)>capacity ? (capacity-size) : left;
        size += inc;
        left -= inc;
        System.out.printf("%s produce(%3d) --> left=%3d, inc=%3d, size=%3d\n", 
            Thread.currentThread().getName(), val, left, inc, size);
        // 告訴“花費者”可以花費了。
        emptyCondtion.signal();
      }
    } catch (InterruptedException e) {
    } finally {
      lock.unlock();
    }
  }

  public void consume(int val) {
    lock.lock();
    try {
      // left 表現“客戶要花費數目”(有能夠花費量太年夜,庫存不敷,需多此花費)
      int left = val;
      while (left > 0) {
        // 庫存為0時,期待“臨盆者”臨盆產物。
        while (size <= 0)
          emptyCondtion.await();
        // 獲得“現實花費的數目”(即庫存中現實削減的數目)
        // 假如“庫存”<“客戶要花費的數目”,則“現實花費量”=“庫存”;
        // 不然,“現實花費量”=“客戶要花費的數目”。
        int dec = (size<left) ? size : left;
        size -= dec;
        left -= dec;
        System.out.printf("%s consume(%3d) <-- left=%3d, dec=%3d, size=%3d\n", 
            Thread.currentThread().getName(), val, left, dec, size);
        fullCondtion.signal();
      }
    } catch (InterruptedException e) {
    } finally {
      lock.unlock();
    }
  }

  public String toString() {
    return "capacity:"+capacity+", actual size:"+size;
  }
};

// 臨盆者
class Producer {
  private Depot depot;

  public Producer(Depot depot) {
    this.depot = depot;
  }

  // 花費產物:新建一個線程向倉庫中臨盆產物。
  public void produce(final int val) {
    new Thread() {
      public void run() {
        depot.produce(val);
      }
    }.start();
  }
}

// 花費者
class Customer {
  private Depot depot;

  public Customer(Depot depot) {
    this.depot = depot;
  }

  // 花費產物:新建一個線程從倉庫中花費產物。
  public void consume(final int val) {
    new Thread() {
      public void run() {
        depot.consume(val);
      }
    }.start();
  }
}

public class LockTest3 { 
  public static void main(String[] args) { 
    Depot mDepot = new Depot(100);
    Producer mPro = new Producer(mDepot);
    Customer mCus = new Customer(mDepot);

    mPro.produce(60);
    mPro.produce(120);
    mCus.consume(90);
    mCus.consume(150);
    mPro.produce(110);
  }
}

(某一次)運轉成果:

Thread-0 produce( 60) --> left= 0, inc= 60, size= 60
Thread-1 produce(120) --> left= 80, inc= 40, size=100
Thread-2 consume( 90) <-- left= 0, dec= 90, size= 10
Thread-3 consume(150) <-- left=140, dec= 10, size= 0
Thread-4 produce(110) --> left= 10, inc=100, size=100
Thread-3 consume(150) <-- left= 40, dec=100, size= 0
Thread-4 produce(110) --> left= 0, inc= 10, size= 10
Thread-3 consume(150) <-- left= 30, dec= 10, size= 0
Thread-1 produce(120) --> left= 0, inc= 80, size= 80
Thread-3 consume(150) <-- left= 0, dec= 30, size= 50

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved