2017-05-18 3 views
-2

연습에서 java 동시성을 읽습니다. 몇 가지 문제가 있습니다. 이해할 수 없습니다. 예를 들어 ,자바 스레드 안전 : 스레드 안전합니까?

package com.thread; 

import java.util.Collections; 
import java.util.HashSet; 
import java.util.Random; 
import java.util.Set; 

public class HiddenIterator { 
    private final Set<Integer> set = Collections.synchronizedSet(new HashSet<Integer>()); 
    public void add(Integer i) { 
     synchronized (set) { 
      set.add(i); 
     } 
    } 
    public void remove(Integer i) { 
     synchronized (set) { 
      set.remove(i); 
     } 
    } 
    public void addTenThings() { 
     Random random = new Random(); 
     for (int i = 0; i < 10; ++i) { 
      add(random.nextInt()); 
     } 
     //Hidden Iterator! 
     System.out.println("DEBUG: added ten elements to " + set); 
    } 
} 

프로그램 스레드는 안전합니까? 그렇지 않으면 편집하는 방법은 무엇입니까?

+0

[Java Concurrency Tutorial] (https://docs.oracle.com/javase/tutorial/essential/concurrency/sync)을 찾으십시오.html) 좋은 가이드가 될 것입니다. –

+0

@ D.B. JCIP만큼 좋지는 않지만 ... – shmosel

+0

@shmosel 아마도 일반적이지 않지만 누군가가 하나의 리소스를 혼란 스럽거나 이해하기 어렵다고 생각하면 다른 것을 읽는 것이 좋습니다. 한 사람이 다른 사람에게 이치에 맞지 않는 것은 당연한 것입니다. –

답변

2

집합에 대한 일부 액세스는 스레드로부터 안전합니다. add 및 에 대한 호출이 동기화되므로 둘 다 동시에 실행할 수 없습니다.

끝 부분의 System.out 행은 메시지를 작성하는 동안 집합에서 toString을 호출하고 toString은 집합의 요소를 반복해야합니다. synchronizedSet을 사용했지만 개별 요소에 대한 액세스 만 보호합니다. 반복하지 않는 동안 집합을 변경하지 않습니다. System.out 행이 실행되는 동안 다른 스레드가 요소를 추가 및 제거하는 경우 메시지에 표시되는 숫자는 예측할 수 없습니다. 메시지를 작성하는 동안 집합의 내용을 "고정"하려면 해당 줄 주위에 synchronized 블록이 필요합니다.

개별add 호출 만 동기화되므로 다른 스레드가 추가되는 개별 항목 사이에 세트를 볼 수 있습니다. 즉, 다른 스레드가 10 개의 항목 중 일부만 가진 목록을 볼 수 있습니다. 프로그램에서 목록을 사용하는 항목에 따라 문제가 될 수도 있고 아닐 수도 있습니다. 다른 스레드가 그 또는 그들 중 누구도 모두가, 당신은 addTenThings 방법 루프 주위에 synchronized 블록을 넣을 수 있습니다 볼 수 있도록

당신이 열 개 요소가 필요한 경우

는, 원자 적으로 추가 할 수 있습니다.

Collections.synchronizedSetsynchronized 블록을 모두 사용할 필요는 없습니다. 둘 중 하나는 OK입니다. 차이점은 : 당신이 필요한 장소에서 동기화하는 것을 잊지 수

  • Collections.synchronizedSet은 세트에 모든 액세스를 보호합니다. 그러나, 그것은 세트의 개별 메소드 호출 만 보호 할 수 있습니다. 특히 루프를 실행하는 동안 다른 스레드가 항목을 추가하거나 제거 할 수 있으므로 집합을 반복하면 예기치 않은 결과가 발생합니다. 하지만 그들은 단지 다른 synchronized 블록으로부터 보호, 그래서 당신은 세트를 액세스하는 모든 코드 주위에 synchronized를 사용해야한다는 점을 기억해야합니다 -
  • synchronized 블록은 원자 작업으로 역할을하도록 여러 방법이 설정에 호출을 보호 할 수 있습니다.
+0

'System.out.println'가'toString'을 통해 세트를 반복 할 것이라는 것을 잊지 마십시오. 이것은 또한 아마도 동기화되어야한다는 것을 의미합니다. –

+0

죄송합니다. 나는 눈치 채지 못했습니다. 좋은 캐치; 수정합니다. – Wyzard

+0

이 답변은 중요한 점을 제기합니다. 장소에있어서의 공식적인 Javadocs를 포함한 많은 것은, 동기 된 메소드가 「thread 세이프」라고하는 타입을 참조 합니다만, 항상 정확하지는 않습니다. 메서드 동기화 만 존재하는 경우, 일반적으로 스레드를 안전하지 않은 방식으로 사용하는 방법이 있습니다. –

1

일부는 너무 안전하고 일부는 충분히 안전하지 않습니다. add()remove()을 명시 적으로 동기화 할 필요가 없습니다. 이는 synchronizedSet 랩퍼에 의해 자동으로 수행되기 때문입니다. 당신이 set을 연결할 때, 그것은 암시 적 요소를 통해 set.toString(), 내부적으로 반복을 호출하기 때문에 설명 된대로

그러나,의 println() 문 주위에 동기화 할 명시적인 동기화없이 안전하지 않다 ("숨겨진 반복자를"), 필요합니까 documentation에 있습니다.