2011-01-11 4 views
1
우리는 멀티 스레드 환경에서, 봄 서비스에서 다음과 같은 문제에 직면하고있다

:자바 일관 동기화

  • 세 가지 목록이 자유롭고 독립적으로 가끔
  • 읽기
  • 에 대한 액세스 (5 분마다), 모두 새로운 값으로 업데이트됩니다. 목록간에 몇 가지 종속성이 있습니다. 예를 들어, 세 번째 것은 읽지 않고 두 번째 것은 업데이트되고 첫 번째 것은 이미 새로운 값을 갖습니다. 세 가지 목록의 일관성을 깨뜨릴 수 있습니다.

초기 아이디어는 3 개의 목록이있는 컨테이너 개체를 속성으로 만드는 것입니다.
그러면 동기화가 먼저 해당 개체에서 수행되고, 세 개의 목록 각각에서 차례로 동기화됩니다.

  • 안전이 초안 (교착 상태, 데이터 일관성)이며,

    일부 코드는 그래서 여기 ... 어 싸우 전드 워즈 가치가 초안

    private class Sync { 
         final List<Something> a = Collections.synchronizedList(new ArrayList<Something>()); 
         final List<Something> b = Collections.synchronizedList(new ArrayList<Something>()); 
         final List<Something> c = Collections.synchronizedList(new ArrayList<Something>()); 
        } 
    
        private Sync _sync = new Sync(); 
    
        ... 
    
        void updateRunOnceEveryFiveMinutes() { 
        final List<Something> newa = new ArrayList<Something>(); 
        final List<Something> newb = new ArrayList<Something>(); 
        final List<Something> newc = new ArrayList<Something>(); 
    
        ...building newa, newb and newc... 
    
        synchronized(_sync) { 
    
         synchronized(_sync.a) { 
          _synch.a.clear(); 
          _synch.a.addAll(newa); 
         } 
    
         synchronized(_sync.b) { ...same with newb... } 
    
         synchronized(_sync.c) { ...same with newc... } 
        } 
    
        // Next is accessed by clients 
    
        public List<Something> getListA() { 
         return _sync.a; 
        } 
    
        public List<Something> getListB() { ...same with b... } 
    
        public List<Something> getListC() { ...same with c... } 
    

    문제가 될 것입니다?

  • 특정 문제에 대한 구현 제안 사항이 더 좋습니까?

업데이트는

는 _sync 동기화 및 newa ... 건물의 순서를 변경.

감사

답변

2

"현재 상태"개체를 노출하고 휘발성을 사용하여 모든 동기화 된 코드를 버리는 것이 가장 좋은 방법입니다. (또한 목록을 수정 불가능한 래퍼로 안전하게 만들 수 있습니다.)

public class ListState { 
    List final a; 
    List final b; 
    List final c; 
} 

private volatile ListState _state; 

void updateRunOnceEveryFiveMinutes() { 
    // generate new lists ... 

    // re-assign current list state (e.g. atomically publish all your updates) 
    _state = new ListState(newA, newB, newC); 
} 

public ListState getCurrentLists() { 
    return _state; 
} 
+0

+1 스레드 동기화 필요 없음, 휘발성 키워드는 로컬 스레드 캐싱을 방지하며, 모든 목록은 동일한 업데이트 된 상태의 클라이언트에 제공됩니다 ... 완벽하게 들립니다! 부정적인 의견/경고 또는 더 나은 (정말로?) 해결책이 게시되지 않는 한, 이것이 수용되는 대답이 될 것입니다. –

1

그것은 다른 (최종) 목록에 복사 만 새 목록을 구축 낭비 조금이다.

불변리스트를 사용 _sync.a, _sync.b_sync.c에 새로운리스트를 할당 고려 목록 작성 후 (그 중 final 개질제를 제거 있음) (updateRunOnceEveryFiveMinutes한다.)

은 또한 4 개의 다른 개체에 동기화된다. 각 메서드 호출에 대해 하나의 개체 (_sync 또는 가능하면 전용 잠금 개체)를 동기화하는 것이 더 간단합니다.

getListA 등으로 반환되는 목록이 불변 인 경우 에 대한 호출이 필요하지 않습니다.

+0

@finnw * 목록 만 작성하는 것은 낭비입니다. * 실제로 목록 (newa ...)의 작성에는 다소 시간이 걸릴 수 있습니다 (동기화 된 파일은 "building newa"), 마지막 복사본은 잠긴 시간을 줄이기위한 것입니다. * 변경된 댓글 * 다음을 참조하십시오! –

+0

@finnw 귀하의 솔루션이 작동한다고 생각하지 않습니다. 목록이 클라이언트에 반환됩니다 ('_sync.a'반환). 따라서 클라이언트는 업데이트 중 * 계속 사용할 수 있습니다 *. 이것이 동기화의 2 번째 레벨이고'Collections.synchronizedList()'를 호출하는 이유입니다. –

+0

@ ring0이 경우 클라이언트는 반복되는 동안 목록에서 수동으로 동기화해야합니다. 'Collections.synchronizedList'에 의해 제공되는 fine-grained 동기화는 하나의 호출 만 동기화합니다. 이것은 충분하지 않을 수 있습니다. – finnw

1

초안은 귀하의 목적을 달성하지 못합니다. 새 A 또는 B가 계산되는 동안 이전 C에 대한 액세스를 금지하는 것은 여기에 없습니다. 이를 수행하는 가장 간단한 방법은 다음과 같습니다.

synchronized (a) { 
    synchronized (b) { 
    synchronized (c) { 
    } 
    } 
} 

주위의 모든 액세스는 읽기 또는 쓰기입니다. 그리고 clear()과 addAll()을하지 않을 것입니다. 변수 값을 newA/newB/newC로 변경하십시오. 위의 값에 약간의 조정이 필요합니다.

+0

교착 상태의 위험이 있으므로 그 구조를 피하고 싶었습니다. 이상적인 해결책이 없다면, 새로운 A와 다른 B를 사용하는 한 스레드의 드문 경우를 갖는 트레이드 오프에 대처하는 아이디어가있을 수 있습니다. 클라이언트 코드가 적절히 적용됩니다 (아직 초안). –

+0

@ ring0 : 해당 구조에 교착 상태가 발생할 위험이 없습니다. 교착 상태는 동기화 순서를 다른 순서로 수행 한 경우에만 발생할 수 있습니다. – EJP

+0

나는이 같은 구조를 만들지는 않을 것입니다. (가능한 한 많이 피하십시오.)하지만 현재의 초안에 맞습니다. 물론 앞으로 어떻게 될지 짐작할 수 없습니다. 그래서 당신은 +1을 얻었습니다 :-) –