hanker

JAVA - 멀티 쓰레드 연산처리 (AtomicInteger) 본문

JAVA

JAVA - 멀티 쓰레드 연산처리 (AtomicInteger)

hanker 2024. 10. 12. 18:28
반응형

 

https://hanke-r.tistory.com/229

 

JAVA - 멀티쓰레드(Multi Thread) 반복문 병렬처리 방법

개발을 하다보면 반복문은 무수히 많이 쓰인다.반복문이 반복횟수가 많아지면 시간이 오래걸리고 보다 더 빠르게 처리해야 되는데, 반복문 안에 있는 코드를 더 이상 최적화 시킬 수 없을 경우

hanke-r.tistory.com

 

이전 글에 이어 멀티쓰레드 처리 시 연산처리를 하는 방법을 알아보자.

 

연산 처리할 경우에 ( java.util.concurrent.atomic )  패키지중 AtomicInteger 클래스를 사용하려고 한다.

AtomicInteger 클래스는 Java 5 버전 이상부터 사용 가능하다.

또한 일반 int와 달리 여러 스레드에서 동시에 접근해도 값이 유지되고, 동기화(synchronization) 없이 스레드의 동시성을 보장한다.

 

코드를 보자(코드는 이전글에 작성했던 코드를 응용해서 작성)

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

    List<Map<String, Object>> list = new ArrayList<>();

    for (int i = 0; i < 200; i++) {
        Map<String, Object> map = new HashMap<>();

        map.put("number", i);
        list.add(map);
    }

    ExecutorService executor = Executors.newFixedThreadPool(8); // 8개의 쓰레드 생성

    AtomicInteger atomicInteger = new AtomicInteger(0);
    for(int i = 0 ; i < list.size() ; i++) {

        final int number = Integer.parseInt(list.get(i).get("number").toString());

        executor.submit(() -> {
            atomicInteger.set(atomicInteger.get() + number);

            System.out.println(Thread.currentThread().getName() + " : " + number);
        });

    }

    executor.shutdown(); // 모든 작업이 끝난 후 쓰레드 종료


    try {
        // 모든 작업이 완료될 때까지 대기 (최대 50초)
        if (executor.awaitTermination(50, TimeUnit.SECONDS)) {

            System.out.println(atomicInteger.get());
        } else {
            executor.shutdownNow();
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

}

추가된 코드를 한줄 한줄 보자

멀티쓰레드 처리하는곳에 final int sum = 0; sum += number; 이렇게 하면 안되나?? 할 수 있겠지만, 안된다. 오류난다

람다 표현식 안에서는 변수가 final 이어야 하는데, final 키워드로 명시된 변수는 선언 후 값 변경이 될 수 없다.

(Variable used in lambda expression should be final or effectively final 오류 발생)

AtomicInteger atomicInteger = new AtomicInteger(0);

- AtomicInteger 초기값 0으로 선언해준다

atomicInteger.set(atomicInteger.get() + number);

- atomicInteger에 저장된 값을 불러와서 number 값이랑 더한 후 다시 atomicInteger에 저장 (+=와 같은 기능)

 

try {
    // 모든 작업이 완료될 때까지 대기 (최대 50초)
    if (executor.awaitTermination(50, TimeUnit.SECONDS)) {

        System.out.println(atomicInteger.get());
    } else {
        executor.shutdownNow();
    }
} catch (InterruptedException e) {
    e.printStackTrace();
}

- executor.awaitTermination 을 사용한 이유가 작업이 완료되기전에 출력되기 때문에, 작업이 완료된 이후에 출력되게 변경

만약 위 코드 없이 출력하였다면 콘솔창에 중간에 더해진 값이 나올것이다. (값도 안맞음)

 

 

위 코드를 실행시켜 보면

 

쓰레드가 전체 다 종료 된 이후 결과 값이 나온걸 확인할 수 있다.

 

AtomicInteger 클래스의 주요 메서드 정리 

1. get() : 현재값 반환 

AtomicInteger atomicInteger = new AtomicInteger(621);
atomicInteger.get();

// 출력 결과 : 621

 

2. set(int value) : 새로운 값으로 설정

AtomicInteger atomicInteger = new AtomicInteger(0);
atomicInteger.set(621);

// atomicInteger = 621

 

3. lazySet(int value) : 새로운 값으로 설정하되, 즉시 반영되지 않고 나중에 반영 (지연설정)

AtomicInteger atomicInteger = new AtomicInteger(0);
atomicInteger.lazySet(621);

 

4. getAndSet(int value) : 현재값 반환 후 새로운 값으로 설정

AtomicInteger atomicInteger = new AtomicInteger(621);
int number = atomicInteger.getAndSet(10); // number = 621 , atomicInteger = 10

 

5. compareAndSet(int expect, int update) : expect 값이 atomicInteger 에 설정된 값과 같으면 update 값으로 변경

AtomicInteger atomicInteger  = new AtomicInteger(621);
boolean result = atomicInteger.compareAndSet(621 , 25); 

// result = true, 값이 25로 변경됨

 

6. getAndIncrement() : 현재 값 반환 후 1증가

AtomicInteger atomicInteger  = new AtomicInteger(621);
int number = atomicInteger.getAndIncrement()

// number = 621, atomicInteger = 622

 

7. incrementAndGet() : 값을 1 증가 시킨 후 반환

AtomicInteger atomicInteger = new AtomicInteger(621);
int number = atomicInteger.incrementAndGet();  

// number = 622

 

8. getAndDecrement() : 현재 값 반환 후 1 감소

AtomicInteger atomicInteger = new AtomicInteger(621);
int number = atomicInteger.getAndDecrement();  

// number = 621, atomicInteger = 620

 

9. decrementAndGet() : 값을 1 감소 시킨 후 반환

AtomicInteger atomicInteger = new AtomicInteger(621);
int number = atomicInteger.decrementAndGet();  

// number = 620

 

10. getAndAdd(int number) : 현재 값을 반환하고 number 만큼 더함

 

AtomicInteger atomicInteger = new AtomicInteger(621);
int number = atomicInteger.addAndGet(9);  

// number = 630

 

 

 

끝.

반응형