2017-05-10 8 views
0

내 의심은 같은 메서드를 사용하는 다른 클래스의 스레드를 동기화하는 방법에 관한 것입니다. 두 개의 다른 클래스, ClientAClientB (분명히 스레드 확장) 및 Server 클래스의 메서드가 있습니다. 두 ClassAClassB의 스레드가이 방법을 사용할 필요가 있지만, 액세스에 대한 다양한 정책이 있습니다 :같은 메서드에서 다른 클래스의 스레드를 동기화하는 방법

  • 경우 ClassA의 스레드가 메소드 내부의 자원을 사용하고는, 같은 클래스의 다른 스레드가 사용할 수 있습니다 ;
  • ClassB의 스레드가이 방법을 사용하는 경우 아무도 (ClassAClassB의 스레드 모두) 사용할 수 없습니다 (상호 배제).

그럼 내 질문입니다.이 동기화 정책을 어떻게 적용 할 수 있습니까? 메소드의 시작 부분에 세마포어 (뮤텍스)를 사용했지만이 해결책에 대해서는 확신 할 수 없습니다. 왜냐하면 매번 모든 유형의 스레드를 차단할 것이라고 생각하기 때문입니다.

+0

[ReadWriteLock] (https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html)이 작업을 수행하지만 시간을 주어야합니다 오버 헤드가 보람 있는지 여부를 알아 내야합니다. – user2357112

+0

답변 해 주셔서 감사합니다. 세마포어를 사용하는 다른 방법이 없다고 생각하십니까? –

답변

0

나는 이것을 달성하기 위해 java.util.concurrent.locks.ReentrantLock을 사용할 수 있다고 생각한다.

예제 조금 다릅니다. 모든 입력 할 수 ClientA, 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

정말 간단한 자바 객체 만 목록에 넣을 수 있습니다. 잠금은 동기화 된 블록에서 완전히 발생합니다. ReentrantLocks를 사용하는 경우 ReentrantLocks를 사용하면 동기화 된 블록과 충돌 할 수 있습니다. –

+0

아, 또한 사용하는 경우 철저히 테스트하십시오. 꽤 단단한 해결책 인 것을 확실히, 그러나 또한 내 머리에서 완전히이다. :) –