Java에서 클래스의 다른 인스턴스를 실행하는 스레드간에 정적 변수를 동기화하는 방법은 무엇입니까?


120

나는 synchronize메서드 앞에 키워드 해당 개체에 동기화가 발생한다는 있습니다. 즉, 개체의 동일한 인스턴스를 실행하는 2 개의 스레드가 동기화됩니다.

그러나 동기화가 개체 수준에 있기 때문에 개체의 다른 인스턴스를 실행하는 2 개의 스레드는 동기화되지 않습니다. 메서드에 의해 호출되는 Java 클래스에 정적 변수가있는 경우 클래스 인스턴스간에 동기화되기를 원합니다. 두 인스턴스는 두 개의 서로 다른 스레드에서 실행됩니다.

다음과 같은 방법으로 동기화 할 수 있습니까?

public class Test  
{  
   private static int count = 0;  
   private static final Object lock= new Object();    
   public synchronized void foo() 
  {  
      synchronized(lock)
     {  
         count++;  
     }  
  }  
}

lock정적 개체 를 정의하고 synchronized해당 잠금에 키워드 를 사용하고 있으므로 정적 변수 count가 이제 클래스의 인스턴스간에 동기화 된다는 것이 사실 Test입니까?


4
잠금 개체가 FINAL로 선언되지 않는 한이 모든 답변은 USELESS입니다!

java.util.concurrent.atomic.AtomicInteger
RoundSparrow hilltx도보십시오.

답변:


193

정적 변수에 대한 액세스를 동기화하는 방법에는 여러 가지가 있습니다.

  1. 동기화 된 정적 방법을 사용하십시오. 이것은 클래스 객체에서 동기화됩니다.

    public class Test {
        private static int count = 0;
    
        public static synchronized void incrementCount() {
            count++;
        }
    } 
  2. 클래스 개체에서 명시 적으로 동기화합니다.

    public class Test {
        private static int count = 0;
    
        public void incrementCount() {
            synchronized (Test.class) {
                count++;
            }
        }
    } 
  3. 다른 정적 개체에서 동기화합니다.

    public class Test {
        private static int count = 0;
        private static final Object countLock = new Object();
    
        public void incrementCount() {
            synchronized (countLock) {
                count++;
            }
        }
    } 

잠금 개체가 클래스 외부에 노출되지 않기 때문에 대부분의 경우 방법 3이 가장 좋습니다.


1
1. 첫 번째는 잠금 개체도 필요하지 않습니다. 최고 여야하지 않습니까?
Baiyan 황

4
2. volatile은 변수가 동기화되었는지 확인하기 때문에 volatile로 카운트를 선언하면 작동합니다.
Baiyan 황

9
# 3이 가장 좋은 이유는 임의의 코드 비트가 동기화되어 Test.class잠재적으로 하루를 망칠 수 있기 때문 입니다. 또한 클래스 초기화는 보유한 클래스에 대한 잠금으로 실행되므로 미친 클래스 이니셜 라이저가 있으면 골치 아픈 일이 될 수 있습니다. 읽기 / 수정 / 쓰기 시퀀스이기 때문에 volatile도움이되지 않습니다 count++. 다른 답변에서 언급했듯이 java.util.concurrent.atomic.AtomicInteger여기에서 올바른 선택 일 가능성이 큽니다.
fadden

4
다른 스레드에서 설정 한대로 올바른 값을 읽으려면 카운트에서 읽기 작업을 동기화하는 것을 잊지 마십시오. 휘발성 (동기화 된 쓰기에 추가)을 선언하는 것도 도움이 될 것입니다.
n0rm1e 2013

1
@Ferrybig 아니요,을 (를) 잠그고 있습니다 Test.class. this동기화 된 비 정적 메서드에 대한 잠금이됩니다.
user3237736

64

단순히 카운터를 공유하는 경우 AtomicInteger 또는 java.util.concurrent.atomic 패키지의 다른 적절한 클래스를 사용하는 것이 좋습니다 .

public class Test {

    private final static AtomicInteger count = new AtomicInteger(0); 

    public void foo() {  
        count.incrementAndGet();
    }  
}

3
1.6이 아닌 Java 1.5에서 사용할 수 있습니다.
Pawel 2013

4

그래, 사실이야.

클래스의 두 인스턴스를 만드는 경우

Test t1 = new Test();
Test t2 = new Test();

그런 다음 t1.foo 및 t2.foo는 모두 동일한 정적 개체에서 동기화되므로 서로를 차단합니다.


주의를 기울이면 한 번에 서로가 아닌 다른 하나를 차단합니다.
Jafar Ali

0

클래스를 통해 코드를 동기화 할 수 있습니다. 그게 가장 간단합니다.

   public class Test  
    {  
       private static int count = 0;  
       private static final Object lock= new Object();    
       public synchronized void foo() 
      {  
          synchronized(Test.class)
         {  
             count++;  
         }  
      }  
    }

이 답변이 유용하기를 바랍니다.


2
이것은 작동하지만 @Fadden의 다른 곳에서 언급했듯이 다른 스레드도 동기화되어 Test.class동작 에 영향을 줄 수 있습니다. 이것이 동기화 lock가 선호되는 이유 입니다.
sbk

당신이 말하는 것이 맞습니다. 그렇기 때문에 위의 방법이 가장 간단한 접근 방식이라고 분명히 언급했습니다.
Jafar Ali

0

ReentrantLock 을 사용 하여 정적 변수에 대한 동기화를 수행 할 수도 있습니다 .

public class Test {

    private static int count = 0;
    private static final ReentrantLock reentrantLock = new ReentrantLock(); 
    public void foo() {  
        reentrantLock.lock();
        count = count + 1;
        reentrantLock.unlock();
    }  
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.