- 자바 동기화(synchronized) 기본 예
1. 문제가 되는 코드
- 기본 코드
public class SimpleSynchronized {
int balance = 100000000;
int count1 = 0;
int count2 = 0;
public int getBalance() {
return balance;
}
public void addBalance(int balance) {
this.balance += balance;
}
public static void main(String[] args) {
final SimpleSynchronized simpleSynchronized = new SimpleSynchronized();
new Thread() {
@Override
public void run() {
while (simpleSynchronized.getBalance() >= 1000) {
simpleSynchronized.addBalance(-1000);
simpleSynchronized.count1++;
}
System.out.println("Thread 1 : " + simpleSynchronized.count1);
}
}.start();
new Thread() {
@Override
public void run() {
while (simpleSynchronized.getBalance() >= 1000) {
simpleSynchronized.addBalance(-1000);
simpleSynchronized.count2++;
}
System.out.println("Thread 2 : " + simpleSynchronized.count2);
}
}.start();
new Thread() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("balance : " + simpleSynchronized.balance);
System.out.println("count total : " + (simpleSynchronized.count1 + simpleSynchronized.count2));
}
}.start();
}
}
- 결과
Thread 2 : 13312
Thread 1 : 89300
balance : -1000
count total : 102612
> 쓰레드1과 쓰레드2가 동일한 시간에 balance 값이 1000 보다 큰 경우만 -1000을 하면 결과적으로 1000 > 0 > -1000이 되는 문제가 발생한다.
> 루프 카운트도 100000000 / 1000 번을 하는 것이 아닌 것이 확인된다.
2. synchronized 적용
- 기본코드
public class SimpleSynchronized {
int balance = 100000000;
int count1 = 0;
int count2 = 0;
public int getBalance() {
return balance;
}
public void addBalance(int balance) {
this.balance += balance;
}
public static void main(String[] args) {
final SimpleSynchronized simpleSynchronized = new SimpleSynchronized();
new Thread() {
@Override
public void run() {
synchronized (simpleSynchronized) {
while (simpleSynchronized.getBalance() >= 1000) {
simpleSynchronized.addBalance(-1000);
simpleSynchronized.count1++;
}
}
System.out.println("Thread 1 : " + simpleSynchronized.count1);
}
}.start();
new Thread() {
@Override
public void run() {
synchronized (simpleSynchronized) {
while (simpleSynchronized.getBalance() >= 1000) {
simpleSynchronized.addBalance(-1000);
simpleSynchronized.count2++;
}
System.out.println("Thread 2 : " + simpleSynchronized.count2);
}
}
}.start();
new Thread() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("balance : " + simpleSynchronized.balance);
System.out.println("count total : " + (simpleSynchronized.count1 + simpleSynchronized.count2));
}
}.start();
}
}
- 결과
Thread 2 : 0
Thread 1 : 100000
balance : 0
count total : 100000
> 쓰레드2는 쓰레드1이 simpleSynchronized 객체를 동기화 하기 때문에 쓰레드1의 작업이 완료되기 전까지 대기 한다.
> synchronized는 동기가 필요한 곳에 적절하게 적용해야 한다.
> 주된 예로 다중쓰레드 환경에서 Map 객체의 put과 remove를 synchronized없이 처리하면 문제가 되는 경우가 있다.
3. 동기화 기본 문법
- 객체 동기화
Object object = new Object();
synchronized (object) {
}
synchronized (this) {
}
> 기본변수(int, long, double 등) 타입은 적용 불가.
- 클래스 단위의 동기화
synchronized (Object.class) {
}
- 메소드 동기화
public synchronized void setObject(Object object) {
}