문제

나는 꽤 많이 읽었지만 결정적인 대답을 찾지 못했습니다.

다음과 같은 수업이 있습니다.

    public class Foo() {

        private static final HashMap<String, HashMap> sharedData;

        private final HashMap myRefOfInnerHashMap;

        static {
           // time-consuming initialization of sharedData
           final HashMap<String, String> innerMap = new HashMap<String, String>;
           innerMap.put...
           innerMap.put...
           ...a

           sharedData.put(someKey, java.util.Collections.unmodifiableMap(innerMap));
        }

        public Foo(String key) {
            this.myRefOfInnerHashMap = sharedData.get(key);
        }

        public void doSomethingUseful() {
            // iterate over copy
            for (Map.Entry<String, String> entry : this.myRefOfInnerHashMap.entrySet()) {
                ...
            }
        }
     }

그리고 FOO의 인스턴스에서 SharedData에 액세스하는 것이 스레드 안전한지 궁금합니다 (생성자와 dosomethingUseful ()에 표시됨). FOO의 많은 인스턴스가 다중 스레드 환경에서 만들어 질 것입니다.

내 의도는 SharedData가 정적 이니셜 라이저에서 초기화되고 그 이후에 수정되지 않았다는 것입니다 (읽기 전용).

내가 읽은 것은 불변의 물체가 본질적으로 스레드 안전하다는 것입니다. 그러나 나는 인스턴스 변수의 맥락 인 것처럼 보였습니다. 불변의 정적 변수 스레드가 안전합니까?

내가 찾은 다른 구성은 동의어입니다. 유형의 concurrenthashmap의 sharedData를 만들 수는 있지만 포함 된 해시 맵도 유형의 conscreshashmap이어야합니까? 원래..

private static final ConcurrentHashMap<String, HashMap> sharedData;

또는

private static final ConcurrentHashMap<String, ConcurrentHashMap> sharedData;

아니면 더 안전할까요 (단순히 복제하는 데 더 많은 비용이 듭니까 ())?

this.myCopyOfData = sharedData.get(key).clone();

티아.

(더 많은 컨텍스트를 제공하기 위해 정적 이니셜 라이저가 편집되었습니다.)

도움이 되었습니까?

해결책

그만큼 참조 에게 sharedData 최종적으로는 스레드가 안전합니다. 지도의 내용은입니다 아니다 스레드는 안전하게 구아바로 싸여 있어야하기 때문에 안전합니다. ImmutableMap 구현 또는 java.util.Collections.unmodifiableMap() 또는 맵 구현 중 하나를 사용하십시오 java.util.concurrent 패키지.

당신이하는 경우에만 둘 다 지도에 포괄적 인 스레드 안전이 있습니까? 포함 된지도는 불변 또는 동시 구현 중 하나 여야합니다.

.Clone ()은 근본적으로 깨졌고 멀리 떨어져 있습니다

기본적으로 복제는 얕은 복제품이며, 단지 전체 사본이 아닌 컨테이너 객체에 대한 참조를 반환합니다. 왜 그 이유에 대한 일반적으로 이용 가능한 정보에 잘 문서화되어 있습니다.

다른 팁

정적 초기화 블록에서 정적 최종 필드의 초기화는 스레드 안전합니다. 그러나 정적 최종 기준 포인트가 할 수있는 객체는 ~ 아니다 스레드 안전합니다. 당신이 참조하는 대상이 스레드 안전 (예 : 불변)이라면 분명합니다.

귀하의 외부 해시 맵에 포함 된 각 개별 해시 맵은 귀하의 질문에 제안 된대로 ConsurenTashMap을 사용하지 않는 한 스레드 안전을 보장하지 않습니다. 스레드-안전 내부 해시 맵 구현을 사용하지 않으면 두 스레드가 동일한 내부 해시 맵에 액세스 할 때 의도하지 않은 결과를 얻을 수 있습니다. ConcurrEthashMap의 일부 작업 만 동기화됩니다. 예를 들어, 반복은 스레드 안전이 아닙니다.

스레드 안전 란 무엇입니까? 물론, 해시 맵의 초기화는 모든 foo의 공유와 동일한 맵 인스턴스를 공유하고 정적 초기에서 예외가 발생하지 않는 한 맵이 보장된다는 점에서 스레드 안전입니다.

그러나지도의 내용을 수정하는 것은 가장 확실하게 스레드 안전하지 않습니다. 정적 최종이란 다른 맵에 대해 맵 SharedData를 전환 할 수 없음을 의미합니다. 그러나지도의 내용은 다른 질문입니다. 주어진 키가 동시에 두 번 이상 사용되면 동시성 문제가 발생할 수 있습니다.

아니요. 불변이 아닌 경우를 제외하고.

그들이하는 유일한 일은입니다

  • 클래스 레벨이 적합합니다
  • 참조를 변경하지 마십시오.

여전히 속성이 변하는 경우 스레드 안전하지 않습니다.

또한보십시오: 최종 인스턴스 변수를 동기화합니까?

클래스 레벨을 제외하고는 정확히 동일합니다.

예, 이것은 스레드 안전입니다. 정적 클래스의 모든 최종 멤버는 스레드에 액세스하기 전에 초기화됩니다.

만약 static 초기화 중에 블록이 실패합니다 ExceptionInInitializerError 먼저 초기화를 시도하는 스레드에서 발생합니다. 이후에 클래스를 참조하려는 시도는 a NoClassDefFoundError.

일반적으로 a HashMap 스레드에서 가시성을 보장하지 않습니다. 그러나 클래스 초기화 코드는 a를 사용합니다 synchronized 여러 스레드가 클래스를 초기화하는 것을 방지하기 위해 차단합니다. 이 동기화는 맵의 상태를 플러시합니다 (및 HashMap 포함 된 인스턴스) 모든 스레드에 올바르게 볼 수 있도록 맵을 변경하지 않거나 클래스 이니셜 라이저 외부에서 포함 된 맵을 변경하지 않도록합니다.

참조 Java 언어 사양, §12.4.2 클래스 초기화 및 동기화 요구 사항에 대한 정보.

본질적으로 a에 대해 안전한 스레드는 없습니다 final static 변하기 쉬운. 멤버 변수 선언 final static 이 변수가 한 번만 할당되도록합니다.

스레드 안전 문제는 변수를 선언하는 방법과 관련이 없지만 대신 변수와 상호 작용하는 방법에 의존합니다. 따라서 프로그램에 대한 자세한 내용 없이는 질문에 답할 수 없습니다.

  • 여러 스레드가 귀하의 상태를 수정하십시오 sharedData 변하기 쉬운?
  • 그렇다면 모든 글에 동기화합니까 (그리고 읽기) sharedData?

동의어를 사용하면 Map 스레드 안전 이며이 스레드-안전과 같은 작업을 수행하지 않습니다.

if (!map.containsKey("foo")) {
    map.put("foo", bar);
}

실제로 정적 초기화가 sharedData 스레드가 안전하고 한 번만 실행됩니까?

그리고 그렇습니다.

물론 여기 많은 사람들이 sharedData 여전히 수정할 수 있습니다.

이 경우 SharedData 객체 만 Immutable이므로 동일한 객체로 작업 할 때마다 만 가능합니다. 그러나 그 내부의 모든 데이터는 모든 스레드에서 언제든지 변경 (제거, 추가 등)을 변경할 수 있습니다.

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