URLConnection
InetAdrress
Socket
SeverSocket
코드를 수정해 보시오.
class Ex13_12 {
public static void main(String args[]) {
Runnable r = new RunnableEx12();
new Thread(r).start(); // ThreadGroup에 의해 참조되므로 gc대상이 아니다.
new Thread(r).start(); // ThreadGroup에 의해 참조되므로 gc대상이 아니다.
}
}
class Account {
private int balance = 1000;
public int getBalance() {
return balance;
}
public void withdraw(int money){
if(balance >= money) {
try { Thread.sleep(1000);} catch(InterruptedException e) {}
balance -= money;
}
} // withdraw
}
class RunnableEx12 implements Runnable {
Account acc = new Account();
public void run() {
while(acc.getBalance() > 0) {
// 100, 200, 300중의 한 값을 임으로 선택해서 출금(withdraw)
int money = (int)(Math.random() * 3 + 1) * 100;
acc.withdraw(money);
System.out.println("balance:"+acc.getBalance());
}
} // run()
}
balance(잔액) thread-safe가 되지 않았기 때문에 스레드가 잔액을 삭감하기 전에 balance(잔액)에 접근해 삭감을 해버리고 다시 이 sleep()에서 깨어나 balance(잔액) 을 삭감해버리기 때문에 잔액이 0 이하의 마이너스 값을 가지게 된다.
class T2 {
public static void main(String args[]) {
Runnable r = new RunnableEx12();
new Thread(r).start(); // ThreadGroup에 의해 참조되므로 gc대상이 아니다.
new Thread(r).start(); // ThreadGroup에 의해 참조되므로 gc대상이 아니다.
}
}
class Account {
private int balance = 1000;
public int getBalance() {
return balance;
}
synchronized public void withdraw(int money){
if(balance >= money) {
try { Thread.sleep(1000);} catch(InterruptedException e) {}
balance -= money;
}
} // withdraw
}
class RunnableEx12 implements Runnable {
Account acc = new Account();
public void run() {
while(acc.getBalance() > 0) {
// 100, 200, 300중의 한 값을 임으로 선택해서 출금(withdraw)
int money = (int)(Math.random() * 3 + 1) * 100;
acc.withdraw(money);
System.out.println("balance:"+acc.getBalance());
}
} // run()
}
해결하는 방법은 간단합니다.
공유데이터에 대한 접근과 수정이 이루어지는 메서드에 synchronized 키워드를 리턴타입 앞에 붙여주면 됩니다. 스레드가 먼저 공유데이터나 메서드에 점유하고 있는 상태인 경우 block으로 처리하기 때문에 이외의 스레드의 접근을 막는다.