자바 의 Copy-On-Write 용기

Copy-On-Write 는 COW 라 고 부 르 며 프로그램 디자인 에 사용 되 는 최적화 전략 입 니 다.그 기본 적 인 사 고 는 처음부터 모두 가 같은 내용 을 공유 하고 누군가가 이 내용 을 수정 하려 고 할 때 진정 으로 내용 을 복사 하여 새로운 내용 을 형성 한 다음 에 고 치 는 것 이다.이것 은 시간 지연 게 으 름 전략 이다.JDK 1.5 부터 자바 를 시작 하여 가방 에 CopyOnWrite 체 제 를 사용 하여 이 루어 진 병발 용 기 를 제공 합 니 다.CopyOnWriteArray List 와 CopyOnWriteArray Set 입 니 다.CopyonWrite 용 기 는 매우 유용 하여 매우 많은 동시 다발 장면 에서 사용 할 수 있다.
CopyonWrite 용기 가 뭐 예요?
CopyOnWrite 용 기 는 쓰기 전에 복사 한 용기 입 니 다.일반적인 이 해 는 우리 가 한 용기 에 요 소 를 추가 할 때 현재 용기 에 직접 추가 하지 않 고 현재 용 기 를 복사 하여 새로운 용 기 를 복사 한 다음 에 새로운 용기 에 요 소 를 추가 하고 요 소 를 추가 한 다음 에 원래 용기 의 인용 을 새로운 용기 에 가리 키 는 것 이다.이렇게 하 는 장점 은 현재 용기 에 어떠한 요소 도 추가 하지 않 기 때문에 CopyOnWrite 용 기 를 동시에 읽 을 수 있다 는 것 이다.그래서 CopyonWrite 용기 도 읽 기와 쓰기 가 분 리 된 사상 으로 읽 기와 쓰기 가 다른 용기 입 니 다.
 
CopyOnWriteArrayList 의 실현 원리
CopyOn WriteArray List 를 사용 하기 전에 우 리 는 먼저 그 소스 코드 를 읽 고 그것 이 어떻게 실현 되 었 는 지 알 아 보 자.다음 코드 는 ArrayList 에 요 소 를 추가 합 니 다.추가 할 때 자 물 쇠 를 추가 해 야 합 니 다.그렇지 않 으 면 다 중 스 레 드 를 쓸 때 N 개의 복사 본 을 복사 합 니 다.
public boolean add(T e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {

        Object[] elements = getArray();

        int len = elements.length;
        //       

        Object[] newElements = Arrays.copyOf(elements, len + 1);
        //            

        newElements[len] = e;
        //            

        setArray(newElements);

        return true;

    } finally {

        lock.unlock();

    }

}

final void setArray(Object[] a) {
    array = a;
}

읽 을 때 자 물 쇠 를 추가 할 필요 가 없습니다.읽 을 때 여러 스 레 드 가 Array List 에 데 이 터 를 추가 하고 있 으 면 읽 을 때 오래된 데 이 터 를 읽 습 니 다.쓸 때 오래된 Array List 를 잠 그 지 않 기 때 문 입 니 다.
public E get(int index) {
    return get(getArray(), index);
}

JDK 에서 CopyOnWriteMap 을 제공 하지 않 았 습 니 다.CopyOnWriteArray List 를 참고 하여 하 나 를 실현 할 수 있 습 니 다.기본 코드 는 다음 과 같 습 니 다.
import java.util.Collection;
import java.util.Map;
import java.util.Set;

public class CopyOnWriteMap<K, V> implements Map<K, V>, Cloneable {
    private volatile Map<K, V> internalMap;

    public CopyOnWriteMap() {
        internalMap = new HashMap<K, V>();
    }

    public V put(K key, V value) {

        synchronized (this) {
            Map<K, V> newMap = new HashMap<K, V>(internalMap);
            V val = newMap.put(key, value);
            internalMap = newMap;
            return val;
        }
    }

    public V get(Object key) {
        return internalMap.get(key);
    }

    public void putAll(Map<? extends K, ? extends V> newData) {
        synchronized (this) {
            Map<K, V> newMap = new HashMap<K, V>(internalMap);
            newMap.putAll(newData);
            internalMap = newMap;
        }
    }
}

실현 은 매우 간단 하 다.CopyOnWrite 체 제 를 이해 하면 우 리 는 각종 CopyOnWrite 용 기 를 실현 하고 서로 다른 응용 장면 에서 사용 할 수 있다.
CopyonWrite 의 응용 장면
CopyonWrite 병발 용 기 는 다 중 쓰기 가 적은 병발 장면 을 읽 는 데 사용 된다.예 를 들 어 화이트 리스트,블랙리스트,상품 류 의 방문 과 업데이트 장면,만약 에 우리 가 검색 사이트 가 있다 면 사용 자 는 이 사이트 의 검색 상자 에 키워드 검색 내용 을 입력 하지만 일부 키 워드 는 검색 되 지 않 습 니 다.검색 할 수 없 는 키 워드 는 블랙리스트 에 올 라 가 매일 저녁 한 번 씩 블랙리스트 가 업데이트 된다.사용자 가 검색 할 때 현재 키워드 가 블랙리스트 에 없 는 지 확인 합 니 다.있 으 면 검색 할 수 없 음 을 알려 줍 니 다.구현 코드 는 다음 과 같 습 니 다:
package com.ifeve.book;

import java.util.Map;

import com.ifeve.book.forkjoin.CopyOnWriteMap;

/**
 *      
 *
 * @author fangtengfei
 *
 */
public class BlackListServiceImpl {

    private static CopyOnWriteMap<String, Boolean> blackListMap = new CopyOnWriteMap<String, Boolean>(
            1000);

    public static boolean isBlackList(String id) {
        return blackListMap.get(id) == null ? false : true;
    }

    public static void addBlackList(String id) {
        blackListMap.put(id, Boolean.TRUE);
    }

    /**
     *        
     *
     * @param ids
     */
    public static void addBlackList(Map<String,Boolean> ids) {
        blackListMap.putAll(ids);
    }

}

코드 는 간단 하지만 CopyOnWriteMap 을 사용 하려 면 두 가지 일 을 주의해 야 합 니 다.
1.확장 비용 을 줄인다.실제 수요 에 따라 CopyOnWriteMap 의 크기 를 초기 화하 여 쓸 때 CopyOnWriteMap 의 확장 비용 을 피 합 니 다.
2.대량 추가 사용.매번 추가 할 때마다 용기 가 매번 복사 되 기 때문에 추가 횟수 를 줄 이면 용기 의 복사 횟수 를 줄 일 수 있다.위 코드 에 있 는 addBlackList 방법 을 사용 하면
CopyonWrite 의 단점
CopyonWrite 용 기 는 장점 이 많 지만 메모리 점용 문제 와 데이터 일치 성 문제 도 두 가지 문제 가 존재 합 니 다.그래서 개발 할 때 주의 가 필요 합 니 다.
메모리 사용량 문제.CopyOnWrite 의 쓰기 시 복사 메커니즘 때문에 쓰기 작업 을 할 때 메모리 에 두 대상 의 메모리,오래된 대상 과 새로 쓴 대상 이 동시에 주둔 합 니 다.(주의:복사 할 때 용기 의 인용 만 복사 하고 쓸 때 새 대상 을 만들어 새 용기 에 추가 합 니 다.오래된 용기 의 대상 은 사용 하고 있 습 니 다.그래서 두 개의 대상 메모리 가 있 습 니 다.만약 에 이 대상 들 이 차지 하 는 메모리 가 비교적 크다 면,예 를 들 어 200 M 정도 이다.그러면 100 M 데 이 터 를 다시 써 넣 으 면 메모리 가 300 M 을 차지한다.그러면 이 럴 때 빈번 한 Yong GC 와 Full GC 를 초래 할 수 있다.이전에 저희 시스템 에서 하나의 서 비 스 를 사 용 했 습 니 다.매일 밤 CopyOn Write 체 제 를 사용 하여 큰 대상 을 업데이트 하기 때문에 매일 밤 15 초의 Full GC 를 만 들 었 고 응용 응답 시간 도 길 어 졌 습 니 다.
메모리 점용 문제 에 대해 서 는 용기 에 있 는 요 소 를 압축 하 는 방법 으로 대상 의 메모리 소 모 를 줄 일 수 있 습 니 다.예 를 들 어 요소 가 모두 10 진법 의 숫자 라면 36 진법 이나 64 진법 으로 압축 하 는 것 을 고려 할 수 있 습 니 다.또는 CopyOnWrite 용 기 를 사용 하지 않 고 다른 병발 용 기 를 사용 합 니 다.예 를 들 어 ConcurrentHashMap.
데이터 일치 성 문제.CopyonWrite 용 기 는 데이터 의 최종 일치 성 만 보장 할 수 있 을 뿐 데이터 의 실시 간 일치 성 을 보장 할 수 없습니다.따라서 원 하 는 데 이 터 를 읽 을 수 있 습 니 다.CopyOnWrite 용 기 를 사용 하지 마 십시오.

좋은 웹페이지 즐겨찾기