동시에 목록에 액세스하기 위해 Java 6에서 사용하는 최상의 접근 방식

StackOverflow https://stackoverflow.com/questions/207829

  •  03-07-2019
  •  | 
  •  

문제

여러 스레드에서 목록 개체에 액세스하고 있습니다. 대부분 하나의 스레드가 있으며 일부 조건에서는 목록을 업데이트하는 두 개의 스레드가 있습니다. 처리중인 사용자 요청 수에 따라이 목록에서 읽을 수있는 1 ~ 5 개의 스레드가 있습니다. 이 목록은 수행해야 할 작업 대기열이 아니며 동시에 검색되고 업데이트되는 도메인 개체 목록입니다.

이제이 목록에 액세스 할 수있는 몇 가지 방법이 있습니다.
-동기화 된 블록을 사용합니다
-정상을 사용하십시오 자물쇠 (즉, 읽기 및 쓰기 OPS는 동일한 잠금을 공유합니다)
-사용 readwritelock
-새로운 것을 사용하십시오 동시에 컬렉션 수업

내 질문:
크리치 탈 섹션에는 일반적으로 많은 작업이 포함되어 있지 않다는 점에서 사용하기위한 최적의 접근법은 무엇입니까 (주로 목록에서 요소를 추가/제거/삽입 또는 가져 오기 만하면)?
위에 나열되지 않은 다른 접근법을 추천 할 수 있습니까?

일부 제약
-최적의 성능은 중요하며 메모리 사용량은 그리 많지 않습니다.
-주문 목록이어야합니다 (현재 동기화 배열 목록), 정렬 된 목록은 아니지만 (예 : 비교 가능 또는 비교기를 사용하여 정렬되지는 않지만 삽입 순서에 따라)
-이 목록은 최대 100000 개의 도메인 개체를 포함하여 크기가 크기 때문에 복사일 WriteArraylist와 같은 것을 사용하여 가능하지 않습니다.
-쓰기/업데이트 원인 섹션은 일반적으로 매우 빠르며 간단한 추가/제거/삽입 또는 교체 (세트)를 수행합니다.
-읽기 작업은 주로 요소 (인덱스) 호출을 대부분 시간에 호출합니다. 일부 읽기 작업은 이진 검색 또는 indexof (요소)를 수행 할 수 있습니다.
-Sodecof (..)과 같은 작업이 트래버스 목록에 포함되지만 목록에 대한 직접 반복이 완료되지 않음

도움이 되었습니까?

해결책

순차적 목록을 사용해야합니까? 맵 유형 구조가 더 적절한 경우 ConcurrentHashMap. 목록과 함께 a ReadWriteLock 아마도 가장 효과적인 방법 일 것입니다.

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

다른 팁

독서 스레드는 무엇을하고 있습니까? 그들이 목록을 반복하고 있다면, 반복 프로세스 전체 동안 아무도 목록에 터치하지 않아야합니다. 그렇지 않으면 매우 이상한 결과를 얻을 수 있습니다.

필요한 의미를 정확하게 정의 할 수 있다면 문제를 해결할 수 있어야하지만 적절하고 효율적으로 수행하려면 자신의 수집 유형을 작성해야한다는 것을 알 수 있습니다. 대안 적으로, 복사 writearraylist 잠재적으로 비싸면 충분히 좋을 수도 있습니다. 기본적으로 요구 사항을 더 많이 묶을수록 더 효율적일 수 있습니다.

이것이 문제에 대한 가능한 솔루션인지는 모르겠지만 ... 데이터베이스 관리자를 사용하여 막대한 양의 데이터를 보유하고 트랜잭션을 관리하도록하는 것이 합리적입니다.

나는 두 번째 Telcontar의 제안 데이터베이스의 데이터베이스는 실제로 이러한 규모의 데이터를 관리하고 스레드 간 협상을 위해 설계되었으므로 메모리 내 컬렉션은 그렇지 않습니다.

데이터는 서버의 데이터베이스에 있고 클라이언트의 로컬 목록은 사용자 인터페이스를 위해 사용한다고 말합니다. 클라이언트에 10 만 항목을 모두 한 번에 보관하거나 복잡한 편집을 수행 할 필요는 없습니다. 클라이언트에서 원하는 것은 데이터베이스에 대한 경량 캐시 인 것 같습니다.

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

예, 이것은 상당히 복잡한 솔루션입니다. 그것의 구성 요소는 다음과 같습니다.

  • 모든 데이터 범위를로드하기위한 프로토콜, 모든 것이 아니라 항목 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... 

이것은 쉬운 솔루션입니다. 나는 더 복잡한 솔루션에 의존하기 전에 이것을 시도 할 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top