2017-05-10 54 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

哦,并且确保你使用它的时候彻底地测试它。很确定这是一个可靠的解决方案,但它也完全来自我的头脑。 :) –