웹사이트 검색

Java의 AtomicInteger


오늘은 Java의 AtomicInteger에 대해 살펴보겠습니다. 원자성 작업은 다른 작업의 간섭 없이 단일 작업 단위에서 수행됩니다. 데이터 불일치를 피하기 위해 다중 스레드 환경에서 원자적 작업이 필요합니다.

AtomicInteger

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) {
        // processing some job
        try {
            Thread.sleep(i * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

위의 프로그램을 실행하면 count 값이 5,6,7,8 사이에서 변하는 것을 알 수 있습니다. 그 이유는 count++가 원자 연산이 아니기 때문입니다. 따라서 한 스레드가 값을 읽고 1씩 증가할 때까지 다른 스레드는 잘못된 결과로 이어지는 이전 값을 읽었습니다. 이 문제를 해결하려면 카운트에 대한 증가 연산이 원자적임을 확인해야 합니다. 동기화를 사용하여 이를 수행할 수 있지만 Java 5 java.util.concurrent.atomic은 int 및 long에 대한 래퍼 클래스를 제공합니다. 동기화를 사용하지 않고 이 원자적 작업을 달성하는 데 사용할 수 있습니다.

자바 AtomicInteger 예제

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

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) {
        // processing some job
        try {
            Thread.sleep(i * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

원자적 작업에 동시성 클래스를 사용하면 동기화에 대해 걱정할 필요가 없다는 이점이 있습니다. 이렇게 하면 코드 가독성이 향상되고 오류 가능성이 줄어듭니다. 또한 원자적 작업 동시성 클래스는 리소스 잠금과 관련된 동기화보다 더 효율적이라고 가정합니다.