2008-10-16 3 views
7

여러 스레드가 List 오브젝트를 액세스하도록했습니다. 대부분 하나의 쓰레드가 있고 어떤 상황에서는리스트를 갱신하는 두 개의 쓰레드가있다. 처리중인 사용자 요청 수에 따라이 목록에서 읽을 수있는 스레드가 1 ~ 5 개 있습니다. 목록은 수행 할 작업 큐가 아니며 동시에 검색 및 업데이트되는 도메인 개체의 목록입니다.
-use 동기화 블록 정상 잠금 -use
(즉, 읽기 및 쓰기 작전 공유 같은 잠금)
-use 하는 ReadWriteLock :리스트가 동시에 액세스 될 때 Java 6에서 사용하기에 가장 좋은 방법

지금이 목록 스레드 안전에 대한 액세스를 할 수있는 몇 가지 방법이 있습니다 새로운 ConcurrentBLABLBA 컬렉션 클래스의
-use 하나

내 질문 :
cricital 섹션에는 일반적으로 많은 작업 (주로 목록에 요소를 추가/제거/삽입 또는 가져 오기)이 포함되어 있지 않으므로 최적의 방법은 무엇입니까?
위에 나열되지 않은 다른 방법을 권장 해 주실 수 있습니까? 하지 정렬 된 목록 (예를 사용하여 정렬되지는 않았지만

일부 제약을
는 -optimal 성능이 중요하다, 메모리 사용량을 너무 많이하지
는 - 그것은, 정렬 된 목록 (현재 ArrayList를 동기를)해야합니다 비교 가능 또는 비교 자이지만 삽입 순서에 따라)
- 목록이 커서 최대 100000 개의 도메인 객체가 포함되어 있으므로 CopyOnWriteArrayList를 사용할 수 없습니다.
쓰기/업데이트 계층 섹션은 일반적으로 매우 빠르며 간단한 추가 작업을 수행합니다/제거/삽입 또는 바꾸기 (설정)
- 대부분의 경우 읽기 작업은 elementAt (index) 호출을 주로 수행하지만 일부 읽기 작업은 이진 검색을 수행 할 수도 있고 indexOf (element)
일 수도 있습니다. indexOf (..)는 목록을 트래버스합니다.

답변

3

순차적 목록을 사용해야합니까? 지도 유형 구조가 더 적합한 경우 ConcurrentHashMap을 사용할 수 있습니다. 목록으로는 ReadWriteLock이 가장 효과적인 방법 일 것입니다.

OP 편집을 반영하여 편집 : 게재 신청서에 대한 이진 검색? 이진 검색에서 타임 스탬프를 저장하고 비교를 위해 사용합니까? 그렇다면 키로 타임 스탬프를 사용할 수 있고 키 순서를 유지하는 컨테이너로 ConcurrentSkipListMap을 사용할 수 있습니다.

+0

나는 ConcurrentSkipListMap 아이디어를 좋아합니다. 90 %의 시간에 목록은 일부 타임 스탬프 (각 도메인 객체의 ID의 일부)에 따라 정렬되므로,이를 최적화하는 것이 좋습니다. 나머지 10 %는 여전히 생각할 것입니다. –

1

읽고 쓰레드는 무엇을하고 있습니까? 목록을 반복하는 경우 반복 과정 전체에서 아무도 해당 목록에 닿지 않도록해야합니다. 그렇지 않으면 아주 이상한 결과가 발생할 수 있습니다.

필요한 의미를 정확하게 정의 할 수 있다면 문제를 해결할 수 있어야합니다. 그러나 올바르게 그리고 효율적으로 자신의 컬렉션 유형을 작성해야 할 필요가 있음을 잘 알고있을 것입니다. 또는 CopyOnWriteArrayList이 잠재적으로 값이 비싸지 만 충분히 양호 할 수 있습니다. 기본적으로 요구 사항을 묶을수록 효율성이 높아집니다.

+0

CopyOnWriteArrayList를 살펴 봤지만 사용하기에는 너무 비쌉니다. 목록은 잠재적으로 100000 개의 요소를 가질 수 있으며 많이 업데이트됩니다. –

+2

그래, 당신은 정말로 당신의 의미를 신중하게 다뤄야 할 것입니다. 이 업데이트는 삽입/삭제 또는 요소 교체 만 수행합니까? 추가/삭제중인 경우 목록의 맨 위/꼬리에서 그렇게하고 있습니까? (어떤 경우에는 연결된 목록이 도움이 될 것입니다.) –

1

이이 문제에 대한 추적 할 수없는 가망 솔루션입니다하지만 경우 나도 몰라 ... 그것은 데이터의 엄청난 금액을 유지하기 위해 데이터베이스 관리 프로그램을 사용하고

+0

데이터는 DB 관리자 및 Appserver 레이어에 의해 서버 측에서 관리되지만 최종 사용자에게 어떻게 든 표시해야합니다.이 목록의 managmenet은 데이터가 검색되는 클라이언트 측에서 발생합니다. –

1

I는 트랜잭션을 관리 할 수 ​​있도록 나에게 의미가 있습니다 데이터베이스의 두 번째 Telcontar's suggestion인데 실제로는이 규모의 데이터를 관리하고 스레드 간 협상을 위해 설계 되었기 때문에 인 메모리 콜렉션은 그렇지 않습니다.

데이터가 서버의 데이터베이스에 있다고 말하면 클라이언트의 로컬 목록은 사용자 인터페이스를위한 것입니다. 한 번에 클라이언트에있는 모든 100000 항목을 유지하거나 복잡한 편집을 수행 할 필요는 없습니다. 클라이언트에서 원하는 것은 데이터베이스에 대한 간단한 캐시입니다.

데이터의 현재 하위 집합 만 클라이언트에 한 번에 저장하는 캐시를 작성하십시오. 이 클라이언트 캐시는 자체 데이터에 대해 복잡한 다중 스레드 편집을 수행하지 않습니다. 대신 모든 편집 내용을 서버에 제공하고 업데이트를 수신합니다. 서버에서 데이터가 변경되면 클라이언트는 단순히 오래된 데이터를 잊어 버리고 다시로드합니다. 하나의 지정된 스레드 만이 콜렉션 자체를 읽거나 쓸 수 있습니다. 이 방법으로 클라이언트는 복잡한 편집 자체가 필요하지 않고 서버에서 발생하는 편집 내용을 그대로 반영합니다.

네, 상당히 복잡한 해결책입니다. 그것의 구성 요소는 다음과 같습니다

    이 아니라 모든 일
  • 변경된 데이터
  • 캐시 클래스에 대한 업데이트를 수신하기위한 프로토콜보다, 항목 478712-478901를 데이터의 범위를로드하기위한 프로토콜 말할
  • 서버에 알려진 인덱스로 항목을 저장하는 서버
  • 서버와 통신하는 해당 캐시에 속한 스레드. 이는 수집 자체에 기록하는 전용 스레드
  • 데이터
  • 이로드되었을 때 UI 구성 요소들이 데이터를주고받을 수 있도록 구현되는 인터페이스를 검색 할 때 콜백을 처리하도록 캐시에 속하는 스레드
첫째 자상에서

이 캐시의 뼈는 다음과 같이 보일 수 있습니다 : 당신은 분명, 자신을 위해 기입해야합니다 세부 사항이 많이있다

class ServerCacheViewThingy { 
    private static final int ACCEPTABLE_SIZE = 500; 
    private int viewStart, viewLength; 
    final Map<Integer, Record> items 
      = new HashMap<Integer, Record>(1000); 
    final ConcurrentLinkedQueue<Callback> callbackQueue 
      = new ConcurrentLinkedQueue<Callback>(); 

    public void getRecords (int start, int length, ViewReciever reciever) { 
     // remember the current view, to prevent records within 
     // this view from being accidentally pruned. 
     viewStart = start; 
     viewLenght = length; 

     // if the selected area is not already loaded, send a request 
     // to load that area 
     if (!rangeLoaded(start, length)) 
      addLoadRequest(start, length); 

     // add the reciever to the queue, so it will be processed 
     // when the data has arrived 
     if (reciever != null) 
      callbackQueue.add(new Callback(start, length, reciever)); 
    } 

    class Callback { 
     int start; 
     int length; 
     ViewReciever reciever; 
     ... 
    } 

    class EditorThread extends Thread { 

     private void prune() { 
      if (items.size() <= ACCEPTABLE_SIZE) 
       return; 
      for (Map.Entry<Integer, Record> entry : items.entrySet()) { 
       int position = entry.key(); 
       // if the position is outside the current view, 
       // remove that item from the cache 
       ... 
      } 
     } 

     private void markDirty (int from) { ... } 

     .... 
    } 

    class CallbackThread extends Thread { 
     public void notifyCallback (Callback callback); 
     private void processCallback (Callback) { 
      readRecords 
     } 
    } 
} 

interface ViewReciever { 
    void recieveData (int viewStart, Record[] records); 
    void recieveTimeout(); 
} 

.

import java.util.Collections; 
import java.util.ArrayList; 

ArrayList list = new ArrayList(); 
List syncList = Collections.synchronizedList(list); 

// make sure you only use syncList for your future calls... 

이 쉬운 솔루션입니다 :

+0

답변 해 주셔서 감사합니다. 최대 100000 개의 항목은 우리가 클라이언트로 가져 오는 데이터의 "페이지"입니다. DB에는 수십억 개의 항목이 포함될 수 있습니다. 내가 심각하게 고려해야 할 것은 단 한 개의 스레드로 목록에 액세스하는 것에 대한 조언입니다. 이것은 분명히 물건을 단순화합니다. –

1

당신은 동기화를 구현하는 래퍼를 사용할 수 있습니다. 나는 더 복잡한 해결책에 의지하기 전에 이것을 시도 할 것이다.