다음의 무거운 작업을 수행해야한다고 생각해보자.
public long heavyWork(int th) {
long result = 1;
for (int i = 0; i < Integer.MAX_VALUE; i++) {
result += i;
}
System.out.printf("%d th done: %d\n", th, result);
return result;
}
위의 작업을 총 10번 해서 총 결과를 알고 싶다면 아래와 같이 작업할 수 있다.
public void getSumByNormal() {
long first = System.currentTimeMillis();
long result = 0;
for (int i = 0; i < 10; i++) {
result += heavyWork(i);
}
long second = System.currentTimeMillis();
System.out.printf("normal: >> 소요시간: %d, 결과: %d\n", (second - first), result);
}
위의 작업은 절차적으로 진행되므로 출력되는 로그는 아래와 같다. (값은 overflow가 발생한다.)
0 th done: 2305843005992468482
1 th done: 2305843005992468482
2 th done: 2305843005992468482
3 th done: 2305843005992468482
4 th done: 2305843005992468482
5 th done: 2305843005992468482
6 th done: 2305843005992468482
7 th done: 2305843005992468482
8 th done: 2305843005992468482
9 th done: 2305843005992468482
normal: >> 소요시간: 5894, 결과: 4611685986215133204
무거운 작업은 별도의 스레드로 분리해서 작업하면 효율을 높일 수 있다.
Long d1 = 0L;
class SumThread extends Thread {
int th = 0;
public SumThread(int th) {
this.th = th;
}
@Override
public void run() {
d1+=heavyWork(th);
}
}
d1은 여러 스레드가 작업 결과를 저장할 공유 자원이다.
위 스레드를 이용해서 동시에 10개의 덧샘을 실행 후 결과를 다시 합해보자. 이때 주의할 점은 계산하는 스레드들이 모두 종료하기 전에는 main이 종료하면 안된다. 이를 위해 join() 메서드를 사용할 수 있다.
public void getSumByThread() throws InterruptedException {
long first = System.currentTimeMillis();
List<SumThread> threads = new ArrayList<>();
for (int i = 0; i < 10; i++) {
SumThread st = new SumThread(i);
threads.add(st);
st.start();
}
for (Thread t : threads) { // 각 스레드의 작업이 끝날 때까지 main 메서드의 작업은 종료되어야 한다.
t.join();
}
long second = System.currentTimeMillis();
System.out.printf("normal: >> 소요시간: %d, 결과: %d\n", (second - first), d1);
}
이제 스레드들은 비 순차적으로 작업을 완료되고 작업에 소요된 시간이 대폭 줄어든 것을 볼 수 있다.
3 th done: 2305843005992468482
7 th done: 2305843005992468482
6 th done: 2305843005992468482
1 th done: 2305843005992468482
0 th done: 2305843005992468482
4 th done: 2305843005992468482
2 th done: 2305843005992468482
8 th done: 2305843005992468482
9 th done: 2305843005992468482
5 th done: 2305843005992468482
normal: >> 소요시간: 883, 결과: 2305843005992468482
하지만 작업의 결과는 원래의 결과와 다름을 알 수 있다.
공유자원인 d1에 여러 스레드가 동시에 접근해서 값을 바꾸는 과정에서 문제가 발생한 것이다.
스레드들의 공유자원 접근 문제를 해결하기 위해 synchronized 키워드를 이용해서 동기화 처리가 필요하다.
class SumThread extends Thread {
int th = 0;
public SumThread(int th) {
this.th = th;
}
@Override
public void run() {
//d1+=heavyWork(th);
add(heavyWork(th));
}
}
public synchronized void add(long unitSum) {
d1 += unitSum;
}
동기화 처리 후 작업 결과를 보면 정확한 합계를 확인할 수 있다.
2 th done: 2305843005992468482
9 th done: 2305843005992468482
6 th done: 2305843005992468482
5 th done: 2305843005992468482
3 th done: 2305843005992468482
1 th done: 2305843005992468482
4 th done: 2305843005992468482
7 th done: 2305843005992468482
8 th done: 2305843005992468482
0 th done: 2305843005992468482
normal: >> 소요시간: 850, 결과: 4611685986215133204