15

これは、(私が思うに) 同じことを達成する 2 つのコードの塊です。

私は基本的に、Java 1.5 の同時実行性を使用して Thread.sleep(long) から逃れる方法を学ぼうとしています。最初の例では ReentrantLock を使用し、2 番目の例では CountDownLatch を使用しています。私がやろうとしていることの要点は、別のスレッドで状態が解決されるまで、1 つのスレッドをスリープ状態にすることです。

ReentrantLock は、他のスレッドをウェイクアップするかどうかを決定するために使用しているブール値にロックを提供し、次に条件を await/signal と共に使用して他のスレッドをスリープさせます。私が知る限り、ロックを使用する必要がある唯一の理由は、複数のスレッドがブール値への書き込みアクセスを必要とする場合です。

CountDownLatch は ReentrantLock と同じ機能を提供しているようですが、(不要な?) ロックはありません。ただし、必要なカウントダウンが1つだけで初期化することで、意図した用途を乗っ取っているような気がします。複数のスレッドが1つのタスクで待機している場合ではなく、複数のスレッドが同じタスクで動作する場合に使用することになっていると思います。

だから、質問:

  1. ReentrantLock コードで「正しいこと」のためにロックを使用していますか? 1 つのスレッドでブール値にのみ書き込みを行う場合、ロックは必要ですか? 他のスレッドを起動する前にブール値をリセットする限り、問題は発生しませんよね?

  2. このタスクにより自然に適した、ロックを回避するために使用できる CountDownLatch に似たクラスはありますか (このインスタンスではロックを回避する必要があると仮定します)。

  3. このコードを改善するために知っておくべき他の方法はありますか?

例 1:

import java.util.concurrent.locks.*;

public class ReentrantLockExample extends Thread {

//boolean - Is the service down?
boolean serviceDown;

// I am using this lock to synchronize access to sDown
Lock serviceLock; 
// and this condition to sleep any threads waiting on the service.
Condition serviceCondition;

public static void main(String[] args) {
    Lock l = new ReentrantLock();
    Condition c = l.newCondition(); 
    ReentrantLockExample rle = new ReentrantLockExample(l, c);

    //Imagine this thread figures out the service is down
    l.lock();
    try {
        rle.serviceDown = true;
    } finally {
        l.unlock();
    }

    int waitTime = (int) (Math.random() * 5000);
    System.out.println("From main: wait time is " + waitTime);
    rle.start();
    try {
        //Symbolizes some random time that the service takes to come back up.
        Thread.sleep(waitTime);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    //Imagine this thread figures out that the service is back up.
    l.lock();
    try {
        rle.serviceDown = false;
        c.signal();
    } finally {
        l.unlock();
    }

}

//Constructor
public ReentrantLockExample(Lock l, Condition c) {  
    this.serviceLock = l;
    this.serviceCondition = c; 
}

/*
 * Should wait for this imaginary service to come back online.
 */
public void run() {
    System.out.println("Thread: start awaiting");
    serviceLock.lock();
    try {
        while (isServiceDown())
        {           
            serviceCondition.await();
        }
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } finally {
        serviceLock.unlock();
    }
    System.out.println("Thread: done awaiting");
}


private boolean isServiceDown() {       
    return serviceDown;
}
}

例 2:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.*;

public class CountDownLatchExample extends Thread {

    //boolean - Is the service down?
    boolean serviceDown;

    // I am using this latch to wait on the service.
    CountDownLatch serviceLatch; 


    public static void main(String[] args) {
        CountDownLatch cdl = new CountDownLatch(1);     
        CountDownLatchExample cdle = new CountDownLatchExample(cdl);

        //Service goes down.
        cdle.serviceDown = true;        

        int waitTime = (int) (Math.random() * 5000);
        System.out.println("From main: wait time is " + waitTime);
        cdle.start();
        try {
            //Symbolizes some random time that the service takes to come back up.
            Thread.sleep(waitTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //Service comes back up.
        cdle.serviceDown = false;
        cdl.countDown();    
    }

    //Constructor 
    public CountDownLatchExample(CountDownLatch cdl) {  
        this.serviceLatch = cdl;         
    }

    /*
     * Should wait for this imaginary service to come back online.
     */
    public void run() {
        System.out.println("Thread: start awaiting");
        try {
            while (isServiceDown()) {           
                serviceLatch.await();
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("Thread: done awaiting");
    }

    private boolean isServiceDown() {       
        return serviceDown;
    }
}
4

3 に答える 3

9

CountDownLatchは 1 回しか解放できないことを除いて、どちらの方法もほぼ同じです。その後、すべてのawait()呼び出しは即座に戻ります。そのため、ダウンしてアップする可能性のあるサービスを使用している場合は、実際にはCyclicBarrierの方が適切な場合があります。

条件が本当に 1 回限りの取引である場合は、FutureTaskの方が適切です。サービスが利用可能になるまで待機するget()を呼び出すことができ、get() が戻るとすぐにサービスを使用できます。

CountDownLatch は、ロックを使用せずに待機できると述べています。ただし、 CountDownLatch とReentrantLockの両方がAbstractQueuedSynchronizerを使用して実装されています。内部的には、同一の同期と可視性のセマンティクスを提供します。

于 2008-11-26T04:18:20.703 に答える
2

私の意見では、このタスクにはロック/条件変数アプローチの方が適しています。ここにあなたと同様の例があります:http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/Condition.html

ブール値の保護に応答して。volatile(http://www.ibm.com/developerworks/java/library/j-jtp06197.html)を使用できます。ただし、ロックを使用しない場合の問題は、サービスがダウンしている時間に応じて、while(isServiceDown())がオンになることです。条件待機を使用することにより、偽のウェイクアップ(条件のJavaドキュメントで説明)まで、または条件が他のスレッドで通知されるまで、スレッドをスリープするようにOSに指示します。

于 2008-11-26T02:51:57.070 に答える
-2

完全な再入可能ロックされた非同期タスクの例:

コード フロー スキーマを使用:

ここに画像の説明を入力

于 2015-06-28T23:29:04.363 に答える