[Java] : 자바의 동시성 이슈(공유자원 접근)

dohyoungK·2024년 4월 23일
0

면접 스크립트

목록 보기
24/25
post-thumbnail

[Java] : 자바의 동시성 이슈(공유자원 접근)


동시성 이슈

자바는 멀티 스레드를 지원하는 언어이기 때문에 여러 스레드가 자원을 공유하는 경우 문제점이 생길 수 있다.

여러 스레드에서 동시에 자원에 접근하는 경우 Race Condition이 발생하고 서로 자원을 사용하려는 경쟁 상태로 인해 자원을 일관성을 지킬 수 없게 된다.

public class Board {
    private int view = 0;

    public void view() {
        view++;
    }

    public int getView() {
        return view;
    }
}
public class MyThread implements Runnable{

    Board board;

    public MyThread(Board board) {
        this.board = board;
    }

    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            board.view();
        }
    }
}
public class Main {
    public static void main(String[] args) throws InterruptedException {
        Board board = new Board();

        for (int i = 0; i < 100; i++) {
            Thread thread = new Thread(new MyThread(board));
            thread.start();
        }

        Thread.sleep(25000);

        System.out.println(board.getView());
    }
}

예를 들어, 100명의 사람이 한 게시물을 50번 조회할 때 개발자의 생각으로는 조회수가 5000이 나와야 하지만 동시성 이슈로 인해 결과가 5000보다 작은 값이 도출되는 것을 볼 수 있다.

이러한 동시성 문제를 위해 자바는 여러 해결책을 가지고 있다.


1. 암시적 Lock (synchronized 키워드)

synchronized 키워드는 임계영역(자원에 대한 동시 접근을 막고 독점을 보장해주는 영역) 을 만들어 공유 자원에 대한 동시 접근을 막아준다.

synchronized 키워드를 사용하는 방식은 2가지로 특정 영역을 임계영역으로 만들거나 메서드 전체를 임계영역으로 설정할 수 있다.

- 특정 영역

public class Board {
    private Integer view = 0;

    // 특정 영역에 synchronized
    public void view() {
        synchronized (view) {
            view++;
        }
    }
    
    public int getView() {
        return view;
    }
}

- 메서드

public class Board {
    private int view = 0;

    // 메서드에 synchronized
    public synchronized void view() {
        view++;
    }
    
    public int getView() {
        return view;
    }
}

2. 명시적 Lock

Lock 인터페이스를 이용해 Lock 객체의 lock() 메서드를 통해 잠그고, unlock()메서드를 사용해 잠금 해제를 할 수 있다.

public class Board {
    private int view = 0;
    private Lock lock = new ReentrantLock();

    // 명시적 Lock
    public void view() {
        lock.lock();
        try {
            // 임계 영역
            view++;
        } finally {
            lock.unlock();
        }
    }
    
    public int getView() {
        return view;
    }
}

3. Concurrent 패키지

자바의 Concurrent 패키지는 동시성 이슈를 해결하기 위한 다양한 클래스와 인터페이스를 제공하기 때문에 이를 사용하면 내부적으로 스레드 간 안전한 데이터 공유를 보장하므로 동시성 이슈를 예방할 수 있다.

public class Board {
    private AtomicInteger view = new AtomicInteger(0);

    // Concurrent패키지 Atomic 사용
    public void view() {
        view.incrementAndGet();
    }

    public int getView() {
        return view.get();
    }
}

이와 같은 방법들을 사용한다면 처음 개발자의 예상과 같이 5000의 결과가 도출되는 것을 볼 수 있다.

0개의 댓글