2009-07-15 9 views
20

인덱스가 다른 한 스레드가 배열의 다른 인덱스에 쓰는 동안 배열의 한 인덱스에서 읽는 스레드에 동시성 문제가 있습니까?java 배열 스레드 안전

class Test1 
{ 
    static final private int N = 4096; 
    final private int[] x = new int[N]; 
    final private AtomicInteger nwritten = new AtomicInteger(0); 
    // invariant: 
    // all values x[i] where 0 <= i < nwritten.get() are immutable 

    // read() is not synchronized since we want it to be fast 
    int read(int index) { 
     if (index >= nwritten.get()) 
      throw new IllegalArgumentException(); 
     return x[index]; 
    } 
    // write() is synchronized to handle multiple writers 
    // (using compare-and-set techniques to avoid blocking algorithms 
    // is nontrivial) 
    synchronized void write(int x_i) { 
     int index = nwriting.get(); 
     if (index >= N) 
      throw SomeExceptionThatIndicatesArrayIsFull(); 
     x[index] = x_i; 
     // from this point forward, x[index] is fixed in stone 
     nwriting.set(index+1); 
    }  
} 

편집 (이 예는 반드시 단지 내 지점을 설명하기 위해 실제 사용하지 않는 것이 좋습니다) :이 예제를 비판 내 질문없는, 말 그대로 단지 동시에, 하나 개의 인덱스에 경우 배열 액세스를 알고 싶어요 다른 색인의 액세스, 동시성 문제 제기, 간단한 예제를 생각할 수 없었다.

답변

12

당신은 배열을 변경하여 잘못된 상태를 얻을 수는 없지만 두 스레드가 동기화없이 비 휘발성 정수를보고있을 때와 동일한 문제가 발생합니다 (Java 튜토리얼의 Memory Consistency Errors 섹션 참조). 기본적으로 문제는 스레드 1이 공간 i에 값을 쓸 수 있지만 스레드 2가 변경 사항을 볼 때 (또는 경우) 보장 할 수 없다는 것입니다.

클래스 java.util.concurrent.atomic.AtomicIntegerArray은 원하는 작업을 수행합니다.

+0

감사합니다 ... drat, byte 배열을 사용하고 싶었습니다. .... 나는 그냥 동기화 된 메서드를 사용하고 간단하게 유지할 것입니다. –

+2

쓰기보다 읽기가 더 많은 경우 java.util.concurrent.locks.ReadWriteLock을 참조하십시오. –

+0

흥미 롭습니다 ... –

4

예제에는 산문 질문과 다른 많은 내용이 있습니다.

이 질문에 대한 답은 배열의 고유 요소가 독립적으로 액세스된다는 것입니다. 따라서 두 스레드가 다른 요소를 변경하는 경우 동기화 할 필요가 없습니다.

그러나 Java 메모리 모델은 액세스를 동기화하지 않으면 한 스레드에서 작성한 값을 다른 스레드가 볼 수 있음을 보장하지 않습니다.

실제 달성하고자하는 목표에 따라 java.util.concurrent에 이미 수행 할 수있는 클래스가있을 수 있습니다. 코드가 해시 테이블을 관리하는 것과 똑같은 일을하는 것처럼 보이기 때문에 ConcurrentHashMap의 소스 코드를 살펴 보는 것이 좋습니다.

1

write 메서드 만 동기화하는 것은 확실하지 않지만 read 메서드를 비동기로두면 작동하지 않을 수 있습니다. 모든 결과가 실제로는 아니지만 적어도 write에 의해 재정의 된 일부 값을 반환하는 read 메서드로 이어질 수 있습니다.

1

예, 잘못된 캐시 인터리빙은 여전히 ​​멀티 CPU/코어 환경에서 발생할 수 있습니다. 그것을 피하기 위해 몇 가지 옵션이 있습니다

  • 사용 원자 배열의 요소를 설정 (또는 jsr166y 추가 기능
  • 사용 AtomicXYZ [] 배열
  • 를 사용하여 사용자 정의 Java7에서에 안전하지 않은 일 - 개인 라이브러리 하나 개의 휘발성 필드 객체와 객체의 배열을 가지고있다.
  • 사용 jsr166y 부록의 ParallelArray를 대신 알고리즘에
1

읽기()가 동기화되지 않기 때문에 다음과 같은 scen을 가질 수 ARIO :

Thread A enters write() method 
Thread A writes to nwriting = 0; 
Thread B reads from nwriting =0; 
Thread A increments nwriting. nwriting=1 
Thread A exits write(); 

이 보장 원하기 때문에 당신의 변수 주소를 결코 분쟁, 무엇을 (배열 인덱스 문제를 할인) 같은 약 :

int i; 
synchronized int curr(){ return i; } 
synchronized int next(){ return ++i;} 

int read() { 
     return values[curr()]; 
    } 

void write(int x){ 
    values[next()]=x; 
} 
+0

감사합니다.하지만 내 질문 및 시나리오가 발생하지 않습니다. (2 단계와 3 단계는 결코 발생하지 않습니다.) –