들어가며
먼저 자바에서 동시성 문제를 해결하기 위한 방법에는 블로킹 방식과 논블로킹 방식 두가지가 존재합니다.
이 글에서는 블로킹 방식의 Syncronized 키워드와 논블로킹 방식의 Atomic 키워드의 성능을 직접 코드를 작성해서 성능의 차이를 확인해 보려 합니다. 이론으로만 Atomic 키워드를 사용하다는 것이 빠르다는 것을 알고 있었기 때문에, 이번 기회를 통해 직접 성능 테스트를 해보며 두 키워드의 성능 차이를 살펴보겠습니다.
Syncronized
먼저 Syncronized를 썼을때 얼마나 걸리는지 알아 보겠습니다.
class CountTest {
private static int count = 0;
private static AtomicInteger atomicCount = new AtomicInteger(0);
private static long start = System.currentTimeMillis();
private static final int expectedCount = 1000;
@Test
void SyncronizedTest() throws InterruptedException {
for (int i = 0; i < expectedCount; i++) {
new Thread(){
public void run(){
syncronizedIncrement();
}
}.start();
}
Thread.sleep(1400);
assertEquals(expectedCount, count);
}
public synchronized void syncronizedIncrement() {
if (++count == expectedCount) {
System.out.println(System.currentTimeMillis() - start);
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
}
}
Atomic
다음으로 Atomic 키워드인 AtomicInteger를 사용했을때 얼마나 걸리는지 알아 보겠습니다.
class CountTest {
private static int count = 0;
private static AtomicInteger atomicCount = new AtomicInteger(0);
private static long start = System.currentTimeMillis();
private static final int expectedCount = 1000;
@Test
void AtomicTest() throws InterruptedException {
for (int i = 0; i < expectedCount; i++) {
new Thread(){
public void run(){
atomicIncrement();
}
}.start();
}
Thread.sleep(1000);
assertEquals(expectedCount, atomicCount.intValue());
}
public void atomicIncrement() {
if (atomicCount.incrementAndGet() == expectedCount) {
System.out.println(System.currentTimeMillis() - start);
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
}
}
결론
해당 결과를 통해 Syncronized 키워드를 사용하는 것보다 Atomic 키워드를 사용하는 것이 더 빠른것을 알 수 있었다.
위 코드에서는 1ms의 지연을 주어서 이러한 결과가 나왔지만, 실제 개발 환경에서는 이 보다 더 복잡하고 오래 걸리는 비즈니스 로직을 사용하게 됩니다. 그렇게 되면, 현재 나온 결과 보다 더 큰 차이를 보일 수 있습니다.
따라서, 무턱대고 동시성 문제를 해결하기 위해 Syncronized 키워드를 사용하는 것은 시스템의 큰 성능 저하를 불러올 수 있습니다.
그렇다면 Atomic을 사용하는게 무조건 좋을까?
Atomic 클래스들은 빠르고 가볍지만 모든 상황에서 Syncronized를 완전히 대체하기는 어렵습니다. Atomic 클래스들은 주로 간단한 연산에 대한 원자성을 보장하기 때문에, 복잡한 상황이나 조건들이 주어지면 Syncronized를 사용하여 동시성을 해결해야 합니다.
또한, Atomic 연산은 non-blocking방식으로 동작하기 때문에, Busy Wait 문제가 발생할 수 있습니다. 특히, 스레드간의 경합이 많이 일어나는 상황에서 여러 스레드가 동시에 같은 변수를 업데이트하려고 할 때, 실패한 스레드가 성공할 때까지 계속해서 재시도를 하면서 CPU 시간을 낭비할 수 있습니다.
따라서, 상황에 따라 적절한 방법을 선택하는것이 중요합니다.
'Language > Java' 카테고리의 다른 글
Java ArrayList의 동작원리 (0) | 2023.12.18 |
---|---|
불변(Immutable)객체와 동시성 (0) | 2023.10.29 |