Java에서의 AtomicInteger

오늘은 Java에서 AtomicInteger을 살펴볼 것입니다. 원자 연산은 다른 작업의 간섭 없이 단일 작업 단위에서 수행됩니다. 원자 연산은 데이터 불일치를 피하기 위해 다중 스레드 환경에서 필수적입니다.

AtomicInteger

각 스레드가 공유 count 변수를 4번 증가시키는 간단한 다중 스레드 프로그램을 만들어 보겠습니다. 따라서 스레드가 두 개 있다면, 그들이 작업을 마친 후 count 값은 8이어야 합니다. JavaAtomic.java

package com.journaldev.concurrency;

public class JavaAtomic {

    public static void main(String[] args) throws InterruptedException {

        ProcessingThread pt = new ProcessingThread();
        Thread t1 = new Thread(pt, "t1");
        t1.start();
        Thread t2 = new Thread(pt, "t2");
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Processing count=" + pt.getCount());
    }

}

class ProcessingThread implements Runnable {
    private int count;

    @Override
    public void run() {
        for (int i = 1; i < 5; i++) {
            processSomething(i);
            count++;
        }
    }

    public int getCount() {
        return this.count;
    }

    private void processSomething(int i) {
        // 작업 처리
        try {
            Thread.sleep(i * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

위의 프로그램을 실행하면 count 값이 5, 6, 7, 8 사이로 변하는 것을 알 수 있습니다. 그 이유는 count++가 원자적 연산이 아니기 때문입니다. 따라서 한 스레드가 그 값을 읽고 하나를 증가시킬 때까지 다른 스레드가 이전 값을 읽어 오류가 발생합니다. 이 문제를 해결하기 위해 count의 증가 작업이 원자적임을 확인해야 합니다. 이를 위해 동기화를 사용할 수 있지만 Java 5 java.util.concurrent.atomic은 동기화 사용 없이 이러한 원자적 작업을 수행할 수 있는 int와 long의 래퍼 클래스를 제공합니다.

Java AtomicInteger 예제

여기에는 항상 카운트 값이 8로 출력되는 업데이트된 프로그램이 있습니다. 왜냐하면 AtomicInteger 메서드 incrementAndGet()가 현재 값에 1을 원자적으로 증가시키기 때문입니다.

package com.journaldev.concurrency;

import java.util.concurrent.atomic.AtomicInteger;

public class JavaAtomic {

    public static void main(String[] args) throws InterruptedException {

        ProcessingThread pt = new ProcessingThread();
        Thread t1 = new Thread(pt, "t1");
        t1.start();
        Thread t2 = new Thread(pt, "t2");
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Processing count=" + pt.getCount());
    }
}

class ProcessingThread implements Runnable {
    private AtomicInteger count = new AtomicInteger();

    @Override
    public void run() {
        for (int i = 1; i < 5; i++) {
            processSomething(i);
            count.incrementAndGet();
        }
    }

    public int getCount() {
        return this.count.get();
    }

    private void processSomething(int i) {
        // 작업 처리
        try {
            Thread.sleep(i * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

원자 연산에 대한 동시성 클래스 사용의 장점은 동기화에 대해 걱정할 필요가 없다는 것입니다. 이는 코드 가독성을 향상시키고 오류 가능성을 줄입니다. 또한, 원자 연산 동시성 클래스가 리소스를 잠그는 동기화보다 더 효율적이라고 가정됩니다.

Source:
https://www.digitalocean.com/community/tutorials/atomicinteger-java