2011-11-18 4 views
2
public class counting 
{ 
    private static int counter = 0; 

    public void boolean counterCheck(){ 
    counter++; 
    if(counter==10) 
    counter=0; 
    } 
} 

메서드 counterCheck는 내 응용 프로그램의 여러 스레드에서 액세스 할 수 있습니다. 정적 변수는 스레드로부터 안전하지 않다는 것을 알고 있습니다. 누군가가 예제를 통해 나를 도울 수 있거나, 방법 또는 블록을 동기화해야하는 이유를 알려 주시면 감사하겠습니다. 동기화하지 않으면 어떻게됩니까?다중 스레드 응용 프로그램에서 정적 카운터 스레드가 안전합니까?

+4

이것은 코드의 전형적인 예 *하지 * 스레드 실제로 가까운이 스레드가 안전하게 할 수있는 작업은 손실됩니다. –

+1

asnwer가 아니라 두 개의 관련이없는 팁 : (1) 컴파일되지 않습니다. [countCheck()]에 대한 반환 값이 없습니다. (2) java는 ** 클래스 이름이 대문자 **로 시작하는 강력한 명명 규칙을 가지고 있습니다. 이름 바꾸기 :'counting' ->'Counting'. – amit

+0

@ Amit- 이것은 원래 코드가 아닙니다. 생각해 줘서 고마워. – questborn

답변

2

이 스레드로부터 안전하지 않습니다, 여러 스레드에서 카운트를 업데이트하는이 패턴은 아마도 멀티 스레드 응용 프로그램의 (더 많은 스레드를 추가 할 때 느리게 실행) 부정적인 스케일링을 달성하는 # 1 방법입니다.

이 스레드를 안전하게 만들기 위해 필요한 잠금을 추가하면 모든 스레드가 계산 중에 완전히 정지합니다. 원자 적 연산을 사용하여 카운터를 업데이트하더라도 카운터를 업데이트하는 모든 스레드 사이에서 CPU 캐시 라인을 수신 거부하게됩니다.

이제 카운터를 업데이트하기 전에 각 스레드 작업에 상당한 시간이 소요될 경우 문제가되지 않습니다. 그러나 각 작업이 빠르면 카운터 업데이트를 통해 작업이 일련 화되어 모든 스레드에서 모든 작업이 느려집니다. 그 자체로 counter++의 첫

6

확실히 스레드로부터 안전하지 않습니다. 완벽하게 병렬로 실행되는 두 개의 스레드를 고려하십시오. 카운터가 9이면 각 카운터가 증가하여 카운터가 11이됩니다. 둘 다 그 카운터를 10으로 볼 수 없기 때문에 카운터는 의도 한대로 줄 바꿈하지 않고 계속 증가합니다.

+0

이것은 완벽하게 이해할 수 있습니다. – questborn

0

가장 큰 위험? counter == 10 확인 전에 counter 두 번 증분을 설정하면 0으로 재설정되지 않습니다.

counter++; // counter = 10 

스레드 2이 수행합니다 :

0

counter 9.

스레드 1 상상해하는이가하는 지금

counter++; // counter = 11 
if(counter==10) // oops 

을, 당신은 당신이이 문제를 해결할 수 있다고 생각 될 수 있습니다

if(counter >= 10) counter -= 10; 

하지만 이제는 두 스레드가 조건을 검사하여 사실인지 확인하면 두 스레드가 모두 카운터를 10 씩 감소시킵니다 (카운터가 음수).

또는 더 낮은 수준에서

counter++ 실제로 세 가지 작업입니다 : 추가 counter
  • 받기

    • 하나 그래서 counter

    스토어

  • counter에 :

    1. 스레드 1,
    2. 두 스레드가이 상황에서 자신의 카운터

    를 저장하는 스레드 2

  • 이 두 스레드가 자신의 카운터를 추가 카운터를 얻을 수
  • , 당신은 두 번 증가하는 카운터 원하는 카운터를 얻을 수 있지만, 한 번만 증가합니다. 당신이 synchronized 블록을 포장 수, 그래서

    c1 = counter; 
    c2 = counter; 
    c1 = c1 + 1; 
    c2 = c2 + 1; 
    counter = c1; // Note that this has no effect since the next statement overrides it 
    counter = c2; 
    

    을하지만, 더 나은 것 인 AtomicInteger를 사용하면 단 몇 스레드가있는 경우 :이 코드가 실행되는 경우로 상상할 수

    public class counting { 
        private static AtomicInteger counter = new AtomicInteger(0); 
    
        public static void counterCheck() { 
         int value = counter.incrementAndGet(); 
         // Note: This could loop for a very long time if there's a lot of threads 
         while(value >= 10 && !counter.compareAndSet(value, value - 10)) { 
          value = counter.get(); 
         } 
        } 
    } 
    
  • 0

    여러 가지 이유로 스레드로부터 안전하지 않습니다. 가장 확실한 것은 다른 답변에서 언급했듯이 9에서 11로가는 두 개의 스레드를 가질 수 있다는 것입니다.

    그러나 카운터 ++는 원자 연산이 아니므로 동일한 값을 읽고 나중에 동일한 값으로 증가하는 두 개의 스레드를 가질 수 있습니다. (실제로 두 개의 호출이 1 씩 증가 함을 의미).

    하나의 스레드가 여러 수정을하고 다른 스레드가 Java 메모리 모델로 인해 다른 스레드가 레지스터에 캐시 된 값을 볼 수 있기 때문에 다른 스레드는 항상 0을 볼 수 있습니다.

    좋은 경험 법 : 일부 공유 상태가 여러 스레드에 의해 액세스 될 때마다 그 중 하나가이 공유 상태를 수정하기 쉽기 때문에 모든 액세스와 읽기 전용 액세스조차도 동일한 잠금을 사용하여 동기화되어야합니다.

    0

    는 스레드

    하드웨어 제한이

    int tmp = counter; 
    tmp=tmp+1; 
    counter=tmp; 
    

    하는 것이 상당하고 2 개 스레드가있을 때 무슨 일이 동시에이 일이 아닌 것은? 하나의 업데이트는 그

    당신이 atomicInteger와 CAS 루프

    private static AtomicInteger counter = new AtomicInteger(0); 
    
    public static boolean counterCheck(){ 
        do{ 
         int old = counter.get(); 
         int tmp = old+1; 
         if(tmp==10) 
          tmp=0; 
         } 
        }while(!counter.compareAndSet(old,tmp)); 
    } 
    
    관련 문제