3

どちらかを呼び出すかlock.lock()、ブロックに入ろうとすると、synchronized他のスレッドがすでにそのロックを取得している場合、スレッドはブロックされます。ここで私の質問は、その実装を見るとlock.lock()、現在のスレッドを実際にパークする AQS にロックの取得を委任することです (そのため、スケジューラによってそれ以上スケジュールすることはできません)。

synchronizedブロッキングでも同じですか?

スレッドのステータスも違うと思います。たとえば、スレッドがsynchronizedblock でブロックされてBLOCKINGいる場合、 を呼び出し た場合lock.lock()は になりますWAITING。私は正しいですか?

Thread.status私の懸念は、忙しい待機の代わりに駐車することによるパフォーマンスの向上の側面における以下の2つのロック戦略の違いです

  1. ReentrantLock.lock();
  2. synchronize { /*some code */ }
4

3 に答える 3

4

lockorを呼び出すlockInterruptiblyと、スレッドが次のWAITING状態になります。

待機スレッドのスレッド状態。次のいずれかのメソッドが呼び出されたため、スレッドが待機状態になっています。

  • タイムアウトなしの Object.wait
  • タイムアウトなしの Thread.join
  • LockSupport.park

次のコードは 4 つのスレッドを開始します。最初の 2 つ (A、B) は同じコードを実行し、lockメソッドを介していくつかのモニターをロックします。他の 2 つ (C、D) も同じコードを実行しますが、lockInterruptiblyメソッドを介して別のモニターをロックします。

public static synchronized void dumpThreadState(List<Thread> threads) {
    System.out.println("thread state dump start");
   for (Thread t: threads) {
        System.out.println(t.getName()+" "+t.getState());
    } 
   System.out.println("thread state dump end\n");
}

public static void main(String[] args) throws InterruptedException {
    final Lock lock = new ReentrantLock();
    final Lock anotherLock = new ReentrantLock();
    List<Thread> threads = new LinkedList<Thread>();
    
    Runnable first = new Runnable() {

        @Override
        public void run() {                
            try {
                lock.lock();
            } 
            catch (Exception ex) {
                System.out.println(Thread.currentThread().getName()+" processing exception "+ex.getClass().getSimpleName());                    
            }
            while (true);                
        }
    } ;
    Runnable second = new Runnable() {

        @Override
        public void run() {         
            try {
                anotherLock.lockInterruptibly();
            } 
            catch (InterruptedException ex) {
                System.out.println(Thread.currentThread().getName()+" was interrupted");
            }
            while (true); 
        }
    };
    
    threads.add(new Thread(first,"A"));
    threads.add(new Thread(first,"B"));
    threads.add(new Thread(second,"C"));
    threads.add(new Thread(second,"D"));
           
    
    dumpThreadState(threads);
    
    for (Thread t: threads) {
        t.start();
    }
    
    Thread.currentThread().sleep(100);
    
    dumpThreadState(threads);
    
    System.out.println("interrupting " + threads.get(1).getName());
    threads.get(1).interrupt();
     
    dumpThreadState(threads);
    
    System.out.println("interrupting " + threads.get(3).getName());
    threads.get(3).interrupt();
    
    Thread.currentThread().sleep(100);
    dumpThreadState(threads);
    
    for (Thread t: threads) {
        t.join();
    }
}

以下を出力します。

thread state dump start
A NEW
B NEW
C NEW
D NEW
thread state dump end

thread state dump start
A RUNNABLE
B WAITING
C RUNNABLE
D WAITING
thread state dump end

interrupting B
thread state dump start
A RUNNABLE
B WAITING
C RUNNABLE
D WAITING
thread state dump end

interrupting D
D was interrupted
thread state dump start
A RUNNABLE
B WAITING
C RUNNABLE
D RUNNABLE
thread state dump end

ご覧のとおり、メソッドでロックされたスレッドlockは中断できませんが、メソッドでロックされたスレッドは中断lockInterruptiblyできます。

synchronized他の例では、3 つのスレッドが開始され、最初の 2 つ (A、B) は同じコードを実行し、ブロックを介して同じモニターをロックします。3 番目のスレッドは別のモニターでロックしますが、次のwaitメソッドを介して待機します。

 public static void main(String[] args) throws InterruptedException {
    final Object lock = new Object();
    final Object anotherLock = new Object();
    
    List<Thread> threads = new LinkedList<Thread>();
    
    Runnable first = new Runnable() {

        @Override
        public void run() {
            synchronized(lock) {
                while (true);
            }
        }
    } ;
    Runnable second = new Runnable() {

        @Override
        public void run() {         
            synchronized(anotherLock) {
                try {
                    anotherLock.wait();
                } 
                catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        }
    };
    
    threads.add(new Thread(first,"A"));
    threads.add(new Thread(first,"B"));
    threads.add(new Thread(second,"C"));
    
    dumpThreadState(threads);
    
    for (Thread t: threads) {
        t.start();
    }
    
    Thread.currentThread().sleep(100);
    
    dumpThreadState(threads);
    
    for (Thread t: threads) {
        t.join();
    }
    
}

以下を出力します。

thread state dump start
A NEW
B NEW
C NEW
thread state dump end
thread state dump start
A RUNNABLE
B BLOCKED
C WAITING
thread state dump end

スレッド C は次のWAITING状態になり、スレッド B はBLOCKING次の状態になりました。

モニター・ロックを待ってブロックされたスレッドのスレッド状態。ブロック状態のスレッドは、監視ロックが同期ブロック/メソッドに入るのを待っているか、Object.wait を呼び出した後に同期ブロック/メソッドに再び入るのを待っています。

編集:

これは、スレッド状態の非常に優れたUML ダイアグラムです。

于 2013-06-21T13:25:01.327 に答える
4

BLOCKING - リソースでブロックされており、中断できません

WAITING - リソースでブロックされていますが、中断されるか、通知されるか、保留解除される可能性があります。

ご覧のとおり、WAITING は別のプロセスからの制御に適しています。たとえば、2 つのスレッドがデッドロックした場合、割り込みで lock() を解除できます。同期を使用する 2 つのスレッドでは、スタックします。

同期対ロックの動作は非常に似ており、正確な詳細はメジャー リビジョン間で異なります。

私のアドバイスは使用することです

  • スレッドセーフが必要であるがロック競合が非常に少ない単純なコードのために同期されます。

  • ロックの競合があることを確認した場合は Lock を使用するか、tryLock などの追加機能が必要です。


もしあなたがそうするなら

final Lock lock = new ReentrantLock();
lock.lock();
Thread t = new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});
t.start();
Thread.sleep(100);
System.out.println(t + " is " + t.getState());
lock.unlock();

版画

Thread[Thread-0,5,main] is WAITING

Thread.State状態


待機スレッドのスレッド状態。次のいずれかのメソッドが呼び出されたため、スレッドが待機状態になっています。

  • タイムアウトなしの Object.wait
  • タイムアウトなしの Thread.join
  • LockSupport.park

待機状態のスレッドは、別のスレッドが特定のアクションを実行するのを待っています。たとえば、オブジェクトで Object.wait() を呼び出したスレッドは、別のスレッドがそのオブジェクトで Object.notify() または Object.notifyAll() を呼び出すのを待機しています。Thread.join() を呼び出したスレッドは、指定されたスレッドが終了するのを待っています。

于 2013-06-21T11:35:28.677 に答える
3

スレッドのパーキングと同期ブロッキングは大きく異なります。同期ブロックに入ろうとすると、オブジェクト インスタンスのモニターを明示的に取得しようとします。モニターを取得できない場合、スレッドはモニターが使用可能になるまで BLOCKING 状態になります。Object.wait()パーキングは、他の条件が真になるまで続行できないことをコードが認識しているという点で、メソッドに似ています。ここでブロックする意味はありません。続行するための私の条件が現在 true であるため、無駄になるからです。この時点で、通知を受けるまでnotify()(notifyAll()またはunpark())。条件が真になると、待機状態になったら出てきて、おそらくモニターを取得しようとし、必要に応じてブロックに入ります。モニターを手に入れたら、RUNNINGに入り、陽気な道を進みます

つまり、待機とは、私が何かを実行できないことを認識し、他のスレッドが実行可能だと判断したときに通知してもらうことです。目が覚めた後、ブロックにつながる可能性があります。ブロッキングは、明示的な他の前提条件なしでモニターへのアクセスを競合するだけです。

lock()インスタンスでが呼び出されると、Lock呼び出しスレッドは実際には待機状態になり、ブロックされません。ここでの利点は、この待機状態を中断できるため、デッドロックを回避できることです。クラスのようなものを使用すると、 、 、およびを介して、Lock必要な待機動作に関する多数のオプションがあります。待機する時間や、呼び出すメソッドによって中断できるかどうかなどを指定できます。コードでは、そのようなオプションはありません。ブロックしていて、スレッドが必要なモニターを放棄するまでブロックし続け、それが決して放棄されない場合はデッドロックになります。そのため、Java 5 とパッケージ以降、tryLock()tryLock(long,TimeUnit)lock()lockInterruptibly()synchronizedconcurrentsynchronizedキーワードの代わりに、 や などを使用して同様のセマンティクスを実装してみてLockくださいCondition

于 2013-06-21T11:33:56.193 に答える