[Day 15 | Java] 공유 자원의 문제와 동기화

y♡ding·2024년 11월 1일
0

데브코스 TIL

목록 보기
102/163

공유 자원 (Shared Resource)

여러 스레드가 동시에 접근하거나 사용하는 자원으로, 메모리, 변수, 파일, 데이터베이스 연결 등이 포함됩니다.

  • 공유 자원은 여러 사람이 함께 사용하는 공용 공간과 같습니다. 예를 들어, 회사 내 프린터가 여러 사람이 동시에 사용하려고 할 때, 누군가가 인쇄 작업을 완료하기 전까지는 다른 사람이 사용할 수 없도록 해야 데이터의 일관성을 유지할 수 있습니다.
  • 예시: 두 스레드가 동시에 은행 계좌에서 돈을 출금하려고 할 때, 잔고는 두 스레드가 공유하는 자원이 됩니다.

경쟁 상태 (Race Condition)

여러 스레드가 동시에 자원에 접근하여 예기치 않은 결과가 발생하는 상황을 말합니다. 스레드가 경쟁적으로 자원에 접근하면서, 올바르지 않은 값이 저장되거나 데이터가 손상될 가능성이 있습니다.

  • 경쟁 상태는 여러 사람이 동시에 출입구를 통과하려다 혼잡과 충돌이 발생하는 상황과 비슷합니다. 순서를 지키지 않으면 서로 충돌하거나, 원하는 결과를 얻기 어려운 것처럼, 경쟁 상태에서는 데이터의 일관성이 깨질 수 있습니다.
  • 예시: 두 스레드가 동시에 은행 계좌에서 돈을 출금하려고 할 때, 잔고가 1000인 상황에서 각각 800씩 출금하면 잔고가 -600이 되는 문제가 발생할 수 있습니다.

동기화 (Synchronization)

정의: 여러 스레드가 동시에 공유 자원에 접근하지 못하도록 제어하는 메커니즘입니다. 동기화를 통해 한 번에 하나의 스레드만 자원에 접근할 수 있게 하여 데이터 불일치 문제를 방지할 수 있습니다.

  • 동기화는 여러 사람이 공용 자원을 사용할 때 순서를 지키도록 하는 규칙과 같습니다. 예를 들어, 회사의 프린터가 동기화되었다고 가정하면, 한 사람이 인쇄하는 동안 다른 사람은 대기하고, 인쇄가 끝나면 다음 사람이 사용할 수 있는 방식으로 관리됩니다.
  • 예시: synchronized 키워드를 사용하여 메서드나 블록을 동기화하면, 해당 부분을 한 번에 한 스레드만 접근할 수 있습니다.

공유 자원의 문제: 개념과 예시

예를 들어, 은행의 계좌를 관리하는 프로그램을 만든다고 가정해 봅시다. 여기에는 계좌(Account) 클래스가 있고, 두 명의 고객(Client)이 동시에 출금을 시도합니다. 각 고객이 계좌에서 일정 금액을 출금하는 작업을 각각 다른 스레드로 처리하게 됩니다. 계좌의 잔고가 1000원인데, 두 고객이 동시에 800원을 출금하려고 하면 어떤 일이 발생할까요?

예상 문제:

  • 두 스레드가 동시에 출금을 시도하면서, 잔고 확인 후 실제 출금까지 걸리는 시간 동안 다른 스레드가 잔고를 변경할 수 있습니다.
  • 잔고가 부족해도 두 스레드가 각각 800원을 인출하게 되면, 잔고가 -600원이 되는 데이터 불일치 문제가 발생할 수 있습니다.

예제코드

package com.exam7;

public class Account {
    // 통장 잔고 (공유 자원)
    private int balance = 1000;

    // 현재 잔고를 반환하는 메서드
    public int getBalance() {
        return balance;
    }

    // 출금 메서드
    // public void withdraw(int money) {    // 경쟁 상태 발생
    public synchronized void withdraw(int money) {  // synchronized 통해 경쟁상태 문제 해결
        // 잔고가 출금 요청 금액보다 많거나 같은지 확인
        if (balance >= money) {
            // 실제 출금 처리
            try {
                Thread.sleep(1000); // 출금 처리 전 1초 대기 (예: 실제 출금 작업을 시뮬레이션)
                balance -= money;   // 잔고에서 출금 금액을 차감
            } catch (InterruptedException e) {
                System.out.println("[에러] " + e.getMessage()); // 예외 발생 시 에러 메시지 출력
            }
        } else {
            System.out.println("잔고가 없음");
        }
    }
}

package com.exam7;

public class Client implements Runnable {
    // 출금할 계좌
    private Account account;

    // 생성자 - 특정 계좌 객체를 받아 저장
    public Client(Account account) {
        this.account = account;
    }

    @Override
    public void run() {
        // 계좌 잔고가 0보다 클 때까지 출금 시도
        while (account.getBalance() > 0) {
            // 출금할 금액을 100~400 사이의 랜덤 값으로 설정
            int money = (int) (Math.random() * 3 + 1) * 100;
            account.withdraw(money); // 출금 수행

            // 현재 잔고 출력
            System.out.println("통장 잔고 : " + account.getBalance());
        }
    }
}

package com.exam7;

public class ClientEx {
    public static void main(String[] args) {
        Account account = new Account();

        Client client1 = new Client(account);
        Client client2 = new Client(account);

        Thread t1 = new Thread(client1);
        Thread t2 = new Thread(client2);

        System.out.println("시작");

        // 2개의 ATM 동시 인출
        t1.start();
        t2.start();

        System.out.println("끝");
    }
}

0개의 댓글

관련 채용 정보