다수의 thread가 동작하는 muti thread는 자원의 충돌을 방지하기위해 '동기화' 에 대응해줘야하는 숙명을 갖고있습니다. 이번 포스팅에서는 java의 synchronized keyword 로 동기화한 소스코드에 대해 살펴보고 메모리 구조 (stack, heap) 까지 확인해보겠습니다.
1. 동기화
1.1 동기화 정의
- 동기화의 사전적 정의는 작업들(task)사이에 수행시기를 맞추는것이며, thread는 데이터의 충돌이 발생하지 않도록 하기 위해 하나의 thread가 자원을 사용하고 있을 경우 해당 자원을 lock 하여 다른 thread의 접근을 막는것을 의미합니다.
1.2 muti thread 자원의 충돌 원인
- 하나의 자원 (=instance)를 다수의 thread가 사용하는 동작 환경으로 인해 발생
1.3 동기화 종류
- function 동기화 (함수)
- variable 동기화 (변수)
2. function 동기화
2.1 소스코드
// 계좌관리 클래스
class Account{
int balance = 1000; // 계좌 잔액 (DB 역활)
// 잔액을 차감하는 함수 [lock 대상]
public synchronized void withdraw(int money) {
if(balance >= money) {
balance -= money; // 누적 차감
try {
Thread.sleep(1000); // 1000ms=1s
}catch(Exception e) {
e.printStackTrace();
}
}
}
}
// thread 구현체
class RealAccount implements Runnable{
Account acc = new Account(); // 계좌관리 객체 생성 [공통 자원]
// Thread의 멤버 함수 start() 호출 시 해당 함수가 호출됨
@Override
public void run() {
while(acc.balance > 0) {
int money = (int)(Math.random()*3+1) * 100; // 100,200,300 중 렌덤
acc.withdraw(money);
System.out.println("-------------------------------");
System.out.println("["+Thread.currentThread().getName() + "] 잔액 : "+acc.balance);
}
}
}
class ThreadTest {
public static void main(String arg[]) {
Runnable run = new RealAccount();
Thread t1 = new Thread(run, "thread 1"); // RealAccount 구현체 사용
Thread t2 = new Thread(run, "thread 2"); // RealAccount 구현체 사용
t1.start();
t2.start();
}
}
위의 소스는 2개의 thread 가 하나의 자원 (=Account 객체)을 동시에 사용하는 경우입니다. thread 1가 Account 객체의 멤버 함수 (=withdraw함수)를 사용 중 일 때 thread 2가 해당 멤버 함수에 접근하지 못하도록 막아줍니다.
thread 2 는 실행 대기중으로 전환되고 thread 1의 실행이 종료되면 이후 해당 멤버 함수 (=withdraw함수) 를 사용 할 수 있습니다.
* 참고 : 해당 소스에서는 사용한 두개의 thread는 같은 기능을 하는 목적이 있기 때문에, 동일한 구현체 (RealAccount 객체) 를 사용하였으나 별도의 구현체를 정의하여 사용 할 수 도 있습니다.
2.2 실행결과
2.3 논리적 구조
2.4 메모리 구조 (stack, heap)
3. variable 동기화
3.1 소스코드
// thread 구현체
public class RealAccount implements Runnable {
// 다른 위치에서 현재 thread의 상태를 확인하기위한 flag (false : 종료상태, true : 실행상태 )
private static Boolean isExecuting = false;
Account acc = new Account(); // 계좌관리 객체 생성 [공통자원]
@Override
public void run() {
while(true){
int curTime = 0;
Calendar m_Calendar = Calendar.getInstance();
if(!jobCheckin()) return;
curTime = m_Calendar.get(Calendar.HOUR_OF_DAY); //현재 시간 (1, 2, ... )
if( ( curTime == configTime) ){ // 현재시간 == 설정된 시간
try{
int money = (int)(Math.random()*3+1) * 100; // 100,200,300 중 랜덤
acc.withdraw(money);
System.out.println("-------------------------------");
System.out.println("["+Thread.currentThread().getName() + "] 잔액 : "+acc.balance);
}catch(Exception e){
e.printStackTrace();
}
} // if() END
try {
jobCheckout(); // thread 종료상태 전환
Thread.sleep(60 * 60 * 1000 ); // thread state : 1Hour 일시정지
} catch (Exception e) {}
} // while(true) END
} // run() END
private static boolean jobCheckin() {
synchronized (isExecuting) {
if (!isExecuting) {
isExecuting = true;
return true;
} else {
return false;
}
}
}
private static void jobCheckout() {
synchronized (isExecuting) {
isExecuting = false;
}
}
}
위의 소스코드는 thread 구현체 부분이며 해당 구현체를 호출하여 사용하기전에 jobCheckin() 함수로 flag 변수 (=isExecuting)를 확인 후 사용하는 소스코드입니다.
flag 변수를 업데이트 (ture or false) 시 synchronized 키워드로 변수를 lock하여 동기화처리를 해주었습니다.
-------------------------------------------------------------------------------------------------------------
항상 정확한 정보를 공유하고자 노력하고있지만 내용에 개선이 필요한 부분이 있을 수 도 있습니다.
개선이 필요하다고 판단되는 부분이 있을 경우 댓글로 조언해주시면 감사하겠습니다.
* 해당 포스팅이 도움이되셨다면 공감과 댓글 부탁드릴께요 ㅎㅎ
------------------------------------------------------------------------------------------------------------
'Computer Science > common' 카테고리의 다른 글
내부 DDOS 공격에 대한 기록 (JVM OOM) (0) | 2023.09.05 |
---|---|
ElasticSearch 개념 및 구조 (0) | 2020.04.21 |
[Thread] muti Process VS muti Thread (0) | 2020.04.18 |
[Thread] 기초 (0) | 2020.04.18 |
[GIT] git 개념 및 구성 (0) | 2020.01.27 |