2017-05-10 53 views
0

我的疑問涉及同步使用相同方法的不同類的線程的方式。我有兩個不同的類,ClientAClientB(顯然擴展了Thread)和一個Server類的方法。這兩個ClassAClassB線程必須使用此方法,但也有對接入不同的策略:如何在同一方法上同步不同類的線程

  • 如果ClassA線程使用方法內部的資源,同一類的其他線程可以用它;
  • 如果線程ClassB正在使用該方法,則沒有人(線程爲ClassAClassB)可以使用它(互斥)。

所以這是我的問題:我該如何應用這種同步策略?我在方法的開頭使用了一個信號量(互斥量),但我不確定這個解決方案,因爲我認爲這會每次阻塞每種類型的線程。

+0

A [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

這個解決方案看起來比另一個更復雜,但肯定會很有用。我可以使用與'ReentrantLock'關聯的'QueuedThreads'列表而不是'LinkedLIst '嗎?謝謝你的回答! –

+0

你真的只想在列表中簡單的java對象。鎖定完全來自同步塊。如果您按照它們的意圖使用ReentrantLocks,它可能會與同步塊發生衝突。 –

+0

哦,並且確保你使用它的時候徹底地測試它。很確定這是一個可靠的解決方案,但它也完全來自我的頭腦。 :) –