티스토리 뷰
이전에 포스팅한 join() 메서드에서도 언급했듯, Thread는 Main 메서드의 순서와는 별개로 실행된다는 것이 장점이자 단점이다.
즉, 실행 속도에 따라 원치 않는 결과를 얻을 수도 있는 것이다.
간단한 예시로 한 은행 계좌에 두 명의 사람이 각각 입금과 출금을 한다고 가정하고 코드를 짜 보자.
○Bank.java
package com.choonham;
public class Bank {
private int money = 10000; //현재 잔액 초기 값
public Bank() {
// TODO Auto-generated constructor stub
}
/* 입금 처리 메서드 */
public void MoneyIn(int save) {
int m = this.money;
try {
Thread.sleep(3000);
}catch(InterruptedException e) {
System.out.println(e.getMessage());
}
this.setMoney(m + save);
}
/* 출금 처리 메서드 */
public void moneyOut(int needed) {
int m = this.money;
try {
Thread.sleep(200);
}catch(InterruptedException e) {
System.out.println(e.getMessage());
}
this.setMoney(m - needed);
}
public int getMoney() {
return money;
}
private void setMoney(int money) {
this.money = money;
}
}
○Me.java
package com.choonham;
public class Me extends Thread{
public Me() {
// TODO Auto-generated constructor stub
}
public void run() {
MainClass.bnk.MoneyIn(3000);
System.out.println("Money In " + MainClass.bnk.getMoney());
}
}
○Wife.java
package com.choonham;
public class Wife extends Thread{
public Wife() {
// TODO Auto-generated constructor stub
}
public void run() {
MainClass.bnk.moneyOut(1000);
System.out.println("Money Out : " + MainClass.bnk.getMoney());
}
}
○Main.java
package com.choonham;
public class MainClass {
public static Bank bnk = new Bank();
public static void main(String[] args) {
System.out.println("현 잔액 : " + bnk.getMoney());
Me m = new Me();
Wife w = new Wife();
m.start();
w.start();
}
}
a위와 같이 멀티 스레드를 이용하여 Me 클래스(3000원 입금)와 Wife 클래스(1000원 출금)를 Main에서 실행시키게 되면, 정상적인 순서를 따른다면, 3000원 입금 후 1000원을 출금해야 하지만 두 스레드는 각각 따로 실행되어, 공유하는 account의 money변수를 거의 동시에 접근하게 된다.
결과 값을 보면,
○Output:
이렇게 원치 않는 데이터를 얻게 되는 문제점이 있다.
그렇기 때문에 join() 메서드를 사용하여 이를 제어할 수 있지만, join() 메서드는 생성한 Thread 만큼 사용하여 각각 따로, 동기화시켜야 한다는 번거로움을 가지고 있다. (안 되는 건 아니다.)
MainClass만 join()을 추가하여 결과를 확인해보면,
○Main.java:
package com.choonham;
public class MainClass {
public static Bank bnk = new Bank();
public static void main(String[] args) {
System.out.println("현 잔액 : " + bnk.getMoney());
Me m = new Me();
Wife w = new Wife();
m.start();
try {
m.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
w.start();
}
}
○Output:
데이터는 잘 나온다. 하지만 아무리 생각해봐도 Thread가 조금만 많아지면 굉장히 불편할 거 같다.
그래서 사용하는 개념이 바로 run 메서드를 설계할 때부터 동기화시켜 순서를 따르게 하는 동기화(Synchronization)의 기법이다.
synchronized {}는 메서드와 블록 형태로 사용이 가능하다. 즉, synchronized로 묶을 경우, 블록 안에 모든 멤버 변수들은 Lock이 걸려, 순서에 맞춰 실행이 되게 되는 것이다. (뭐, 그럴 거면 왜 Thread를 사용하나 싶긴 하지만, 경우야 많은 거니까..)
동일한 예시로 synchronized를 사용해보면, 2가지 방법으로 동기화를 적용해볼 수 있다.
1. Bank 클래스의 모든 매서드에 적용시킨다.
○Bank.java
package com.choonham;
public class Bank {
private int money = 10000; //현재 잔액 초기 값
public Bank() {
// TODO Auto-generated constructor stub
}
/* 입금 처리 메서드 */
public void MoneyIn(int save) {
synchronized(this) {
int m = this.money;
try {
Thread.sleep(3000);
}catch(InterruptedException e) {
System.out.println(e.getMessage());
}
this.setMoney(m + save);
}
}
/* 출금 처리 메서드 */
public void moneyOut(int needed) {
synchronized(this) {
int m = this.money;
try {
Thread.sleep(200);
}catch(InterruptedException e) {
System.out.println(e.getMessage());
}
this.setMoney(m - needed);
}
}
public int getMoney() {
return money;
}
private void setMoney(int money) {
this.money = money;
}
}
이렇게 적용시키면, Main에서 join을 사용하는 것과 동일한 결과를 얻을 수 있다.
2. Thread를 상속받고 run을 사용하는 class의 run()에 각각 적용시킨다.
○Me.java
package com.choonham;
public class Me extends Thread{
public Me() {
// TODO Auto-generated constructor stub
}
public void run() {
synchronized (this) {
MainClass.bnk.MoneyIn(3000);
System.out.println("Money In " + MainClass.bnk.getMoney());
}
}
}
○Wife.java
package com.choonham;
public class Wife extends Thread{
public Wife() {
// TODO Auto-generated constructor stub
}
public void run() {
synchronized (this) {
MainClass.bnk.moneyOut(1000);
}
System.out.println("Money Out : " + MainClass.bnk.getMoney());
}
}
마찬가지로 동일한 결과 값을 얻어낼 수 있다.
앞으로 프로젝트를 진행하면서 Thread를 포함한 개념들을 얼마나 많이 사용할지는 모르겠지만, 그게 중요한 건 아니니, 잘 알아두도록 하자.
끝!
'[JAVA] > JAVA' 카테고리의 다른 글
<Thread> Join() (0) | 2021.03.31 |
---|---|
<Thread> Priority 지정하기 (0) | 2021.03.31 |
<Thread> Sleep을 이용한 간단한 타이머 구현 (0) | 2021.03.31 |
<Thread> Thread 겉핥기 (0) | 2021.03.30 |
"웹 개발자 양성과정" 03.24 <Interface & Abstract Class> (0) | 2021.03.25 |
- 파니노구스토
- await
- 인천 구월동 이탈리안 맛집
- javascript
- 정보보안기사 #실기 #정리
- react
- AsyncStorage
- Promise
- react-native
- Async
- redux-thunk
- redux
- 인천 구월동 맛집
- 맛집
- 이탈리안 레스토랑
- Total
- Today
- Yesterday