sourcecode

ATOMIC Integer의 실용적 용도

copyscript 2022. 8. 12. 23:33
반응형

ATOMIC Integer의 실용적 용도

AtomicInteger 및 기타 Atomic 변수가 동시접속을 허용하는 것으로 알고 있습니다.그런데 이 클래스는 보통 어떤 경우에 사용됩니까?

두 로 나눌 수 있습니다.AtomicInteger:

  • 로서( 「 「 」 )incrementAndGet()에서 동시에 할 수 , 등)

  • 비교 및 스왑 명령을 지원하는 프리미티브(compareAndSet()을 구현합니다를 사용하여 비표준 알고리즘을 구현합니다.

    다음은 Brian Göetz의 Java Concurrency In Practice에서 나온 논블로킹 난수 생성기의 예입니다.

    public class AtomicPseudoRandom extends PseudoRandom {
        private AtomicInteger seed;
        AtomicPseudoRandom(int seed) {
            this.seed = new AtomicInteger(seed);
        }
    
        public int nextInt(int n) {
            while (true) {
                int s = seed.get();
                int nextSeed = calculateNext(s);
                if (seed.compareAndSet(s, nextSeed)) {
                    int remainder = s % n;
                    return remainder > 0 ? remainder : remainder + n;
                }
            }
        }
        ...
    }
    

    으로는 거의 합니다.incrementAndGet() 임의의 즉, 계산)을 합니다.calculateNext()(어느 쪽인가 하면)

제가 생각할 수 있는 가장 간단한 예는 원자 연산을 증가시키는 것입니다.

표준 ints의 경우:

private volatile int counter;

public int getNextUniqueIndex() {
    return counter++; // Not atomic, multiple threads could get the same result
}

ATOMIC Integer 사용 시:

private AtomicInteger counter;

public int getNextUniqueIndex() {
    return counter.getAndIncrement();
}

후자는 모든 액세스를 동기화할 필요 없이 간단한 돌연변이 효과(특히 카운트 또는 고유 인덱스)를 수행하는 매우 간단한 방법입니다.

복잡한 할 수 .compareAndSet()낙관적인 잠금의 한 종류로서 - 현재 값을 얻고, 이를 기반으로 결과를 계산하고, f 값이 계산을 위해 여전히 사용되는 입력이면 이 결과를 설정하고, 그렇지 않으면 다시 시작합니다. 그러나 계산 예는 매우 유용하며, 나는 자주 사용할 것입니다.AtomicIntegers있는 및 합니다.이것들은 매우 하기 쉽기 플레인 최적화를 할 수 있습니다. 이러한 생성기는 작업이 매우 용이하기 때문에 플레인 최적화를 사용하는 것은 시기상조라고 생각합니다.ints.

같은 을 할 수,ints 적절한 ★★★synchronized 언 , 움움움움움움움움AtomicInteger가 실제 에, 「」나 「 할 때 모든 는 없습니다.인터리브나 모니터의 보유를 걱정할 필요는 없습니다.int로 스레드 를 위반하는 더 getAndIncrement()i++올바른 모니터 세트를 미리 입수할 필요가 있습니다(또는 취득하지 마십시오.

ATOMIC Integer가 가지고 있는 메서드를 보면 ints의 일반적인 조작에 대응하는 경향이 있음을 알 수 있습니다.예:

static AtomicInteger i;

// Later, in a thread
int current = i.incrementAndGet();

는 스레드 세이프 버전입니다.

static int i;

// Later, in a thread
int current = ++i;

메서드는 다음과 같이 매핑됩니다.
++ii.incrementAndGet()
i++i.getAndIncrement()
--ii.decrementAndGet()
i--i.getAndDecrement()
i = xi.set(x)
x = ix = i.get()

밖에도 편리한 .compareAndSet ★★★★★★★★★★★★★★★★★」addAndGet

「 」의 AtomicInteger 때 입니다.단, 스레드세이프 조작은 콘텍스트에 있습니다.synchronized type에 및 .int이지만, 「아토믹」AtomicInteger이 다수 되어 있습니다.int.

한 것은 '마음껏'입니다.getAndXXX ★★★★★★★★★★★★★★★★★」xXXAndGet를 들어.예를 들어.getAndIncrement()는, 「」에 입니다.i++이것은 실제로는 취득, 추가 및 할당의 세 가지 작업을 위한 지름길이기 때문에 원자성이 아닙니다. compareAndSet는 세마포, 잠금장치, 래치 등을 구현하는 데 매우 유용합니다.

「 」의 AtomicInteger는 동기화를 사용하여 동일한 작업을 수행하는 것보다 더 빠르고 읽기 쉽습니다.

간단한 테스트:

public synchronized int incrementNotAtomic() {
    return notAtomic++;
}

public void performTestNotAtomic() {
    final long start = System.currentTimeMillis();
    for (int i = 0 ; i < NUM ; i++) {
        incrementNotAtomic();
    }
    System.out.println("Not atomic: "+(System.currentTimeMillis() - start));
}

public void performTestAtomic() {
    final long start = System.currentTimeMillis();
    for (int i = 0 ; i < NUM ; i++) {
        atomic.getAndIncrement();
    }
    System.out.println("Atomic: "+(System.currentTimeMillis() - start));
}

Java 1.6을 탑재한 PC에서는 atomic 테스트가 3초 만에 실행되며 동기화된 테스트는 약 5.5초 만에 실행됩니다.(「」, 「」)입니다.notAtomic++ 짧습니다는 매우 짧습니다.따라서 동기화에 드는 비용은 운용에 비해 매우 중요합니다.

는 ATOMICity 외에 ATOMIC Integer의 할 수 .Integer를 들면, 「」라고 하는 경우Map스스무스무스무스무스무스무스무스무드

예를 들어, 일부 클래스의 인스턴스를 생성하는 라이브러리가 있습니다.이러한 인스턴스는 서버에 전송되는 명령을 나타내므로 각 인스턴스는 고유한 정수 ID를 가져야 합니다.여러 스레드가 동시에 명령을 전송할 수 있기 때문에 AtomicInteger를 사용하여 ID를 생성합니다.또 다른 접근법은 일종의 잠금과 정규 정수를 사용하는 것이지만, 이는 느리고 우아하지 않습니다.

gabuzo가 말했듯이, 나는 가끔 int를 참조로 전달하고 싶을 때 AtomicIntegers를 사용한다.아키텍처 고유의 코드를 가진 빌트인 클래스이므로 어떤 Mutable보다 쉽고 최적화되어 있을 가능성이 높습니다.내가 빠르게 코드화할 수 있는 정수.그렇다고는 해도, 그것은 학급의 학대라고 느껴집니다.

Java 8에서는 원자 클래스가 두 가지 흥미로운 함수로 확장되었습니다.

  • int getAndUpdate(IntUnaryOperator 업데이트)기능)
  • int updateAndGet(IntUnaryOperator 업데이트)기능)

둘 다 업데이트를 사용하고 있습니다.원자값 업데이트를 수행하는 함수입니다.차이점은 첫 번째 값은 오래된 값을 반환하고 두 번째 값은 새 값을 반환한다는 것입니다.업데이트기능은 표준보다 더 복잡한 "비교 및 설정" 작업을 수행하기 위해 구현될 수 있습니다.예를 들어 atomic 카운터가 0 미만이 되지 않는지 확인할 수 있습니다.일반적으로 동기화가 필요하며, 여기서 코드는 잠기지 않습니다.

    public class Counter {

      private final AtomicInteger number;

      public Counter(int number) {
        this.number = new AtomicInteger(number);
      }

      /** @return true if still can decrease */
      public boolean dec() {
        // updateAndGet(fn) executed atomically:
        return number.updateAndGet(n -> (n > 0) ? n - 1 : n) > 0;
      }
    }

이 코드는 Java Atomic 예제에서 가져온 입니다.

저는 보통 여러 스레드에서 액세스 또는 작성할 수 있는 오브젝트에 ID를 지정해야 할 때 AtomicInteger를 사용합니다.또한 오브젝트 컨스트럭터에서 액세스하는 클래스의 정적 속성으로 사용합니다.

non-blocking locks는 compareAndSwap(CAS)을 사용하여 원자 정수 또는 longs에 구현할 수 있습니다.Tl2」소프트웨어 트랜잭션 메모리」페이퍼에서는, 다음과 같이 설명하고 있습니다.

특수 버전의 쓰기 잠금을 모든 트랜잭션된 메모리 위치에 연결합니다.가장 간단한 형태로 버전화된 쓰기 잠금이란 CAS 조작을 사용하여 잠금을 취득하고 잠금을 해제하는 스토어를 사용하는 단일 단어 스핀록입니다.1개의 비트가 잠겼음을 나타내는 데 필요한 것은 1비트뿐이므로 나머지 잠금 워드를 사용하여 버전 번호를 유지합니다.

그것이 설명하는 것은 먼저 원자 정수를 읽는 것이다.이를 무시된 잠금 비트와 버전 번호로 나눕니다.현재 버전 번호와 다음 버전 번호를 사용하여 잠금비트가 클리어된 상태로 CAS에 쓰려고 합니다.성공할 때까지 루프하면 잠금을 소유하는 스레드가 됩니다.잠금 비트가 지워진 상태에서 현재 버전 번호를 설정하여 잠금을 해제하십시오. 이 문서에서는 스레드가 쓸 때 일관된 읽기 세트를 갖도록 조정하기 위해 잠금 버전 번호를 사용하는 방법에 대해 설명합니다.

이 문서에서는 프로세서가 비교 및 스왑 작업을 하드웨어로 지원하므로 매우 효율적이라고 설명합니다.또, 다음과 같은 주장도 있습니다.

원자 변수를 사용한 비블로킹 CAS 기반 카운터는 낮은 경합에서 중간 경합에서 잠금 기반 카운터보다 성능이 우수합니다.

열쇠는, 동시 액세스와 수정을 안전하게 실시할 수 있는 것입니다.이들은 일반적으로 멀티스레드 환경에서 카운터로 사용됩니다. 도입되기 전에는 동기화된 블록으로 다양한 메서드를 정리한 사용자 작성 클래스여야 했습니다.

AtomicInteger로 식사 철학자의 문제를 해결했습니다.

제 솔루션에서는 AtomicInteger 인스턴스가 포크를 나타내기 위해 사용되었습니다.철학자 1인당 2개씩 필요합니다.각 철학자는 1~5의 정수로 식별됩니다.철학자가 포크를 사용하는 경우 AtomicInteger는 철학자의 값 1 ~5를 유지합니다.그렇지 않으면 포크는 사용되지 않기 때문에 AtomicInteger의 값은 -1입니다.

그런 다음 AtomicInteger는 포크가 비어 있는지 확인하고(value==-1), 비어 있는 경우 포크의 소유자로 설정할 수 있습니다.아래의 코드를 참조해 주세요.

AtomicInteger fork0 = neededForks[0];//neededForks is an array that holds the forks needed per Philosopher
AtomicInteger fork1 = neededForks[1];
while(true){    
    if (Hungry) {
        //if fork is free (==-1) then grab it by denoting who took it
        if (!fork0.compareAndSet(-1, p) || !fork1.compareAndSet(-1, p)) {
          //at least one fork was not succesfully grabbed, release both and try again later
            fork0.compareAndSet(p, -1);
            fork1.compareAndSet(p, -1);
            try {
                synchronized (lock) {//sleep and get notified later when a philosopher puts down one fork                    
                    lock.wait();//try again later, goes back up the loop
                }
            } catch (InterruptedException e) {}

        } else {
            //sucessfully grabbed both forks
            transition(fork_l_free_and_fork_r_free);
        }
    }
}

compareAndSet 메서드는 차단되지 않으므로 throughput을 높이고 더 많은 작업을 수행해야 합니다.아시다시피 Dining Philosopers 문제는 프로세스에 작업을 계속하기 위해 리소스가 필요한 것처럼 리소스에 대한 제어 액세스가 필요할 때 사용됩니다.

compareAndSet() 함수의 간단한 예:

import java.util.concurrent.atomic.AtomicInteger; 

public class GFG { 
    public static void main(String args[]) 
    { 

        // Initially value as 0 
        AtomicInteger val = new AtomicInteger(0); 

        // Prints the updated value 
        System.out.println("Previous value: "
                           + val); 

        // Checks if previous value was 0 
        // and then updates it 
        boolean res = val.compareAndSet(0, 6); 

        // Checks if the value was updated. 
        if (res) 
            System.out.println("The value was"
                               + " updated and it is "
                           + val); 
        else
            System.out.println("The value was "
                               + "not updated"); 
      } 
  } 

출력되는 것은 다음과 같습니다.이전 값: 0 값이 갱신되었으며 값은 6입니다.또 다른 간단한 예:

    import java.util.concurrent.atomic.AtomicInteger; 

public class GFG { 
    public static void main(String args[]) 
    { 

        // Initially value as 0 
        AtomicInteger val 
            = new AtomicInteger(0); 

        // Prints the updated value 
        System.out.println("Previous value: "
                           + val); 

         // Checks if previous value was 0 
        // and then updates it 
        boolean res = val.compareAndSet(10, 6); 

          // Checks if the value was updated. 
          if (res) 
            System.out.println("The value was"
                               + " updated and it is "
                               + val); 
        else
            System.out.println("The value was "
                               + "not updated"); 
    } 
} 

인쇄된 내용은 다음과 같습니다: 이전 값: 0 값이 업데이트되지 않았습니다.

언급URL : https://stackoverflow.com/questions/4818699/practical-uses-for-atomicinteger

반응형