이전에 있었던 문제가 왜 생기는지 한번 분석 ㄱㄱ
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 에 대해서 알아보자.