2017-05-10 3 views
0

疑問は、同じメソッドを使用する異なるクラスのスレッドを同期させる方法に関係します。私は2つの異なるクラス、ClientAClientB(明らかにスレッドを拡張する)とServerクラスのメソッドを持っています。同じクラスの他のスレッドがそれを使用することができ、ClassAのスレッドがメソッド内のリソースを使用している場合同じメソッドで異なるクラスのスレッドを同期させる方法

  • ClassAClassBの両方のスレッドは、このメソッドを使用する必要がありますが、アクセスに関する異なるポリシーがあります;
  • ClassBのスレッドがこのメソッドを使用している場合は、誰も(ClassAClassBのどちらのスレッドも)使用できません(相互排除)。

これは私の質問です。この同期のポリシーはどのように適用できますか?メソッドの冒頭でセマフォー(mutex)を使用しましたが、毎回このタイプのスレッドをすべてブロックすると思うので、この解決策についてはわかりません。

+0

[ReadWriteLock](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html)は仕事をしますが、時間を取らなければなりませんオーバーヘッドが価値があるかどうかを判断する。 – user2357112

+0

お返事ありがとうございます。セマフォーを使う方法がないと思いますか? –

答えて

0

私はあなたがそれを達成するためにjava.util.concurrent.locks.ReentrantLockを使うことができると思います。

例が少し異なります。すべて入力してみましょうクライアントA、ClientBはしていません:

private final ReentrantLock lock = new ReentrantLock(); 
    private void method(boolean exclusive){ 
     log.debug("Before lock() "); 
      try { 
       lock.lock(); 
       if (!exclusive) { 
        lock.unlock(); 
       } 

       log.debug("Locked part"); 
       //Method tasks.... 
       Thread.sleep(1000); 
      }catch(Exception e){ 
       log.error("",e); 
      }finally { 
       if(exclusive) 
        lock.unlock(); 
      } 
     log.debug("End "); 
    } 

実行:

new Thread("no-exclusive1"){ 
    @Override 
    public void run() { 
     method(false); 
    } 
}.start(); 
new Thread("exclusive1"){ 
    @Override 
    public void run() { 
     method(true); 
    } 
}.start(); 
new Thread("exclusive2"){ 
    @Override 
    public void run() { 
     method(true); 
    } 
}.start(); 
new Thread("no-exclusive2"){ 
    @Override 
    public void run() { 
     method(false); 
    } 
}.start(); 

結果:

01:34:32,566 DEBUG (no-exclusive1) Before lock() 
01:34:32,566 DEBUG (no-exclusive1) Locked part 
01:34:32,566 DEBUG (exclusive1) Before lock() 
01:34:32,566 DEBUG (exclusive1) Locked part 
01:34:32,567 DEBUG (exclusive2) Before lock() 
01:34:32,567 DEBUG (no-exclusive2) Before lock() 
01:34:33,566 DEBUG (no-exclusive1) End 
01:34:33,567 DEBUG (exclusive1) End 
01:34:33,567 DEBUG (exclusive2) Locked part 
01:34:34,567 DEBUG (exclusive2) End 
01:34:34,567 DEBUG (no-exclusive2) Locked part 
01:34:35,567 DEBUG (no-exclusive2) End 
+0

だから私は一種のブール "フラグ"を使って進める方法を決めるべきです。私はすでにこの解決策について考えました。答えてくれてありがとう! –

0
たぶん

少し複雑で、私はそれにもかかわらず、どのように証明するために興味深いものになるだろうと思いました同期化されたブロックのみで行うことを検討してください。

public class Server() { 

    private LinkedList<Object> lockQueue = new LinkedList<Object>(); 

    public void methodWithMultipleAccessPolicies(Object client) { 
     Object clientLock = null; 

     // These aren't really lock objects so much as they are 
     // the targets for synchronize blocks. Get a different 
     // type depending on the type of the client. 
     if (client instanceof ClientA) { 
      clientLock = new ALock(); 
     } else { 
      clientLock = new BLock(); 
     } 

     // Synchronize on the "lock" created for the current client. 
     // This will never block the current client, only those that 
     // get to the next synchronized block afterwards. 
     synchronized(clientLock) { 
      List<Object> locks = null; 

      // Add it to the end of the queue of "lock" objects. 
      pushLock(clientLock); 

      // Instances of ClientA wait for all instances of 
      // ClientB already in the queue to finish. Instances 
      // of ClientB wait for all clients ahead of them to 
      // finish. 
      if (client instanceof ClientA) { 
       locks = getBLocks(); 
      } else { 
       locks = getAllLocks(); 
      } 
      // Where the waiting occurs. 
      for (Lock lock : locks) { 
       synchronized(lock) {} 
      } 

      // Do the work. Instances of ClientB should have 
      // exclusive access at this point as any other clients 
      // added to the lockQueue afterwards will be blocked 
      // at the for loop. Instances of ClientA should 
      // have shared access as they would only be blocked 
      // by instances of ClientB ahead of them. 
      methodThatDoesTheWork(); 

      // Remove the "lock" from the queue. 
      popLock(clientLock); 
     } 
    } 

    public void methodThatDoesTheWork() { 
     // Do something. 
    } 

    private synchronized void pushLock(Object lock) { 
     lockQueue.addLast(lock); 
    } 

    private synchronized void popLock(Object lock) { 
     lockQueue.remove(lock); 
    } 

    private synchronized List<Object> getAllLocks() { 
     return new ArrayList<Object>(lockQueue); 
    } 

    private synchronized List<Object> getBLocks() { 
     ArrayList<Object> bLocks = new ArrayList<>(); 
     for (Object lock : lockQueue) { 
      if (lock instanceof BLock) { 
       bLocks.add(lock); 
      } 
     } 
    } 
} 

public class ALock {} 
public class BLock {} 
+0

このソリューションは、他のものよりも複雑に見えますが、確かに便利です。 'LinkedLIst 'の代わりに 'ReentrantLock'に関連付けられた' QueuedThreads'リストを使用できますか?答えてくれてありがとう! –

+0

あなたは本当に単純なJavaオブジェクトをリストに入れたいだけです。同期は完全に同期ブロックから行われます。 ReentrantLocksを使用している場合は、同期ブロックと競合する可能性があります。 –

+0

ああ、また、もしあなたがそれを使うならば、それを徹底的にテストしてください。確かにそれは固い解決策だと確信していますが、それは私の頭からも完全にです。 :) –

関連する問題