Java concurrencyの相互反発ロック動力ノードJava学院の整理


Reentrant Lockの紹介
Reentrant Lockは、重入可能な相互反発ロックであり、また「独占ロック」とも呼ばれる。
名前の通り、Rentrant Lockは同じ時点で一つのスレッドにロックされてしか持ちません。再入場できるという意味は、RentrantLockロックは、単一スレッドで複数回取得できるということです。
RentrantrantLockには「公平ロック」と「非公平ロック」があります。それらの違いは、ロックを取得するメカニズムにおいて公平であるかどうかを示しています。ロックは、競合するリソースを保護するために、複数のスレッドが同時にスレッドを操作することを防止するためにエラーが発生し、Reentrant Lockは同じ時点で一つのスレッドにしか取得できません。Reentraant Lockは、FIFOの待ち行列によって、このロックのすべてのスレッドを取得することを管理している。「公平ロック」の仕組みの下で、スレッドは順番に並んでロックを取得する。「アンフェアロック」はロックが取得可能な状態のとき、自分が行列の先頭にいるかどうかに関わらずロックを取得します。 
RentrantLock関数一覧

//      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()
RentrantLock例
「例1」と「例2」を比較することで、ロックとロックの役割を明確に認識することができます。
例1

 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 // LockTest.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
", 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
", 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
結果分析:
(01)Depotは倉庫です。produceを通じて倉庫に貨物を生産し、consumeを通じて倉庫の中の貨物を消費することができます。独占ロックにより倉庫への相互反発訪問を実現します。(生産/消費)倉庫の中の商品を操作する前に、まずロックを通して倉庫をロックし、操作が終わったらアンロックを通じて解除します。
(02)Producerは生産者類である。Producerのプロデュース関数を呼び出して、新しいスレッドを作って倉庫に製品を生産することができます。
(03)Cusstomerは消費者です。Cusstomerのconsume関数を呼び出して、スレッド消費倉庫の中の製品を新規に作成することができます。
(04)メインスレッドのメーンに、生産者のmProを新設し、消費者のmCusを新設する。それらはそれぞれ倉庫の中で生産/消費製品を生産します。
メインの生産/消費数量によって、倉庫の最終的な残りの製品は50であるべきです。運行結果は私達の予想に合っています。
このモデルには二つの問題があります。
(01)現実的には、倉庫の容量はマイナスであることはあり得ない。しかし、このモデルの倉庫容量はマイナスになり得るので、現実と矛盾しています。
(02)現実的には倉庫の容量に制限がある。しかし、このモデルの容量は確かに制限されていません。
この二つの問題は、どう解決するかを少し話します。まず簡単な例2を見ます。「例1」と「例2」を比較することで、ロック()、アンロック()の用途をより明確に認識することができます。 
例2

 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 // LockTest.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
", 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
", 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」に基づいてロックを外しています。「例2」において、最終的に残りの製品は50ではなく、60である。なぜなら、私たちは倉庫への相互反発訪問を実現していないからです。
例3
「例3」では、「例1」の2つの問題をCoditionを通じて解決します。
この問題を解決するのはCondationを通じてです。CondationはLockと連携して使用する必要があります。Condationのawait()方法により、スレッドをブロックすることができます。Condationのsignal()方法により、呼び覚ましスレッドを[notify()]に類似させることができます。

 import java.util.concurrent.locks.Lock;
  import java.util.concurrent.locks.ReentrantLock;
  import java.util.concurrent.locks.Condition;
  // LockTest.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 = ;
     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 > ) {
         //      ,  “   ”    。
         while (size >= capacity)
           fullCondtion.await();
         //   “       ”(         )
         //   “  ”+“       ”>“    ”, “    ”=“    ”-“    ”。(      )
         //   “    ”=“       ”
         int inc = (size+left)>capacity ? (capacity-size) : left;
         size += inc;
         left -= inc;
         System.out.printf("%s produce(%d) --> left=%d, inc=%d, size=%d
", 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 > ) { // , “ ” 。 while (size <= ) emptyCondtion.await(); // “ ”( ) // “ ”<“ ”, “ ”=“ ”; // ,“ ”=“ ”。 int dec = (size<left) ? size : left; size -= dec; left -= dec; System.out.printf("%s consume(%d) <-- left=%d, dec=%d, size=%d
", 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
以上は小编が皆さんに绍介したJava concurrencyの互斥ロックです。皆さんのために役に立つと思います。ここでも私たちのサイトを応援してくれてありがとうございます。