WEB개발/JAVA
[JAVA] AtomicInteger (thread-safe)
wooyeon06
2021. 8. 13. 14:04
AtomicInteger
AtomicInteger란 원자성을 보장하는 Interger를 의미한다. 멀티 쓰레드 환경에서 동기화 문제를 별도의 synchronized 키워드 없이 해결하기 위해서 고안된 방법이다.
(일반적으로 동기화 문제는 synchronized, Atomic, volatile 세가지 키워드로 해결한다.)
synchronized은 특정 Thread가 해당 블락 전체를 lock 하기 때문에 다른 Thread는 아무작업을 못하고 기다리는 상황이 되어 낭비가 심하다.
그래서 NonBlocking하면서 동기화 문제를 해결하기 위한 방법이 Atomic이다. AtomicInterger 동작의 핵심 원리는 바로 CAS알고리즘(Compare and Swap)에 있다.
현재 쓰레드에 저장된 값과 메인 메모리에 저장된 값을 비교하여 일치하는 경우 새로운 값으로 교체하고, 일치 하지 않는 다면 실패하고 재시도를 한다.
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
/*
* This class intended to be implemented using VarHandles, but there
* are unresolved cyclic startup dependencies.
*/
private static final Unsafe U = Unsafe.getUnsafe();
private static final long VALUE
= U.objectFieldOffset(AtomicInteger.class, "value");
private volatile int value;
/**
* Creates a new AtomicInteger with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicInteger(int initialValue) {
value = initialValue;
}
}
- volatile keyword는 Java 변수를 Main Memory에 저장하겠다라는 것을 명시하는 것입니다.
- 만약에 Multi Thread환경에서 Thread가 변수 값을 읽어올 때 각각의 CPU Cache에 저장된 값이 다르기 때문에 변수 값 불일치 문제가 발생하게 됩니다.
import java.util.concurrent.atomic.AtomicInteger;
public class TestMain {
public static void main(String[] args) {
// TODO Auto-generated method stub
AtomicInteger aInt = new AtomicInteger(10000);
Thread t1 = new Thread(new Runnable() {
public void run() {
for(int i=0 ; i<5000 ; i++) {
try {
int result = aInt.addAndGet(1);
System.out.println("t1 is running : " + result);
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
aInt.addAndGet(1);
for(int i=0 ; i<5000 ; i++) {
try {
int result = aInt.addAndGet(1);
System.out.println("t2 is running : " + result);
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
t1.start();
t2.start();
}
}
> 2개 쓰레드에서 출력되는 result는 thread-safe
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(0);
// 값 증가
atomicInteger.incrementAndGet();
System.out.println("현재 값: " + atomicInteger.get()); // 출력: 현재 값: 1
// 값 감소
atomicInteger.decrementAndGet();
System.out.println("현재 값: " + atomicInteger.get()); // 출력: 현재 값: 0
// 특정 값으로 설정
atomicInteger.set(10);
System.out.println("현재 값: " + atomicInteger.get()); // 출력: 현재 값: 10
// 원자적으로 값 추가
atomicInteger.addAndGet(5);
System.out.println("현재 값: " + atomicInteger.get()); // 출력: 현재 값: 15
// 원자적으로 값 감소
atomicInteger.getAndAdd(-3);
System.out.println("현재 값: " + atomicInteger.get()); // 출력: 현재 값: 12
}
}
https://javaplant.tistory.com/23
https://nesoy.github.io/articles/2018-06/Java-volatile