Java adv1 - 동시성 문제 2

dev1·2024년 11월 28일

이전에 있었던 문제가 왜 생기는지 한번 분석 ㄱㄱ

package thread.sync;

import static util.MyLogger.log;
import static util.ThreadUtils.sleep;

public class BankMain {

    public static void main(String[] args) throws InterruptedException {
        BankAccount account = new BankAccountV1(1000);

        Thread t1 = new Thread(new WithdrawTask(account, 800), "t1");
        Thread t2 = new Thread(new WithdrawTask(account, 800), "t2");

        t1.start();
        t2.start();

        sleep(500);
        log("t1 state : " + t1.getState());
        log("t2 state : " + t2.getState());

        t1.join();
        t2.join();

        log("최종 잔액 : " + account.getBalance());

    }
}

검증로직을 확인하고나서, 잔액의 변경을 하는데에 약간의 시간 ...

즉, 바로 변경이 된다라고치면 문제없이 될건데....

예를들어, t1 이 먼저 실행됐다고하고,이후에 t2 가 실행됐다고 하자.

그렇다면, t1 이 해당계좌에서 800원을 출금하게되고, 이후에 검증로직을 확인 ...

그 다음에, sleep(500) 을 통해서 잠깐 대기상태로 ...

이러한 중에, t2 도 로직검증 ... 이후에, sleep(500) 이 끝나고나서, t1 스레드를 통해, 해당은행계좌의 잔액을 변경 ( 200 )

이럴때에 t2 가 검증로직에 걸리면, 문제없겠지만... 컴퓨터의 연산속도는 굉장히 빠르기때문에

값이 변경되기도 전에, t2 도 sleep(500) 으로 들어올 수 있음

즉, 이렇게되면 t2 는 아직 잔액이 1000원으로 되어져있기때문에 출금하는 문제가 생겼던거임

그러면? sleep( 500) 을 없애면 되지않을까?

확인 ㄱㄱ

package thread.sync;

import static util.MyLogger.log;
import static util.ThreadUtils.sleep;

public class BankMain {

    public static void main(String[] args) throws InterruptedException {
        BankAccount account = new BankAccountV1(1000);

        Thread t1 = new Thread(new WithdrawTask(account, 800), "t1");
        Thread t2 = new Thread(new WithdrawTask(account, 800), "t2");

        t1.start();
        t2.start();

        log("t1 state : " + t1.getState());
        log("t2 state : " + t2.getState());

        t1.join();
        t2.join();

        log("최종 잔액 : " + account.getBalance());
    }
}

sleep( 500) 제거 해줬음

이후에 다시돌려봐도...

14:22:29.287 [     main] t1 state : RUNNABLE
14:22:29.287 [       t1] 거래 시작 : BankAccountV1
14:22:29.287 [       t2] 거래 시작 : BankAccountV1
14:22:29.288 [     main] t2 state : WAITING
14:22:29.294 [       t2] [검증을 시작합니다] 출금액 : 800 보유중인 금액 : 1000
14:22:29.294 [       t2] [검증 완료] 출금액 : 800 보유중인 금액 : 1000
14:22:29.294 [       t2] [처리중]
14:22:29.294 [       t1] [검증을 시작합니다] 출금액 : 800 보유중인 금액 : 1000
14:22:29.295 [       t1] [검증 완료] 출금액 : 800 보유중인 금액 : 1000
14:22:29.295 [       t1] [처리중]
14:22:30.300 [       t2] [검증 완료] 출금액 : 800 출금 후 잔액 : 200
14:22:30.301 [       t1] [검증 완료] 출금액 : 800 출금 후 잔액 : -600
14:22:30.301 [       t2] 거래를 종료합니다.
14:22:30.302 [       t1] 거래를 종료합니다.
14:22:30.306 [     main] 최종 잔액 : -600

문제가 마찬가지로 발생

그럼 어떻게해야 해결? 우선, 임계영역부터 알아보자 ㄱㄱ

우선, 이전에있었던 문제에 대해서 조금 더 알아보자.

문제가 생겼던 원인 ? ==> 여러스레드가 같은 객체에 접근해서 문제가 발생 ...

그러면, 하나의 스레드만 접근하게하고, 그 스레드가 로직을 완료하면 그 다음에 다른 스레드를 접근할수있게 ?

즉, 하나의 스레드가 출금시도하고, 출금완료되면 그 다음에 다른스레드를 접근할 수 있게...

==> balance ( 금액 ) 이 변경된 이후에 다른 스레드가 들어오니까 ... 로직이 정상적으로수행

다시 돌아와서, 그럼 임계영역은 무엇일까?

==> 어떠한 코드를 진행하는 과정중에 한개의 스레드만 접근할 수 있게 ...

즉, 이러한 것을 전제로 해야하는 코드영역 => 임계영역

다시한번 정리하면, 임계영역은 ==> 특정코드가 진행되는 과정중에 한개의 스레드만 접근할 수 있어야하는 코드영역

그렇다면, 이 임계영역을 어떻게 보장할까? ( 어떻게 로직중에 한개의 스레드만 접근할 수 있게 할수있을까? )

==> synchronized 키워드를 통해서 가능함

그럼이제, synchronized 에 대해서 알아보자.

0개의 댓글