[Java] Multithread Programming
Multithread
thread가 둘 이상인 프로그램을 Multithread라고 한다.
main 메소드의 첫 번째 명령문부터 실행 (main thread) 이 시작된다.
main thread 는 program 이 시작되면 자동으로 시작되지만, 다른 thread 는 main thread 에서 만들어 시작해야 함.
이 thread 들은 시작되고 나면 main thread 와 동등한 레벨이 됨.
main thread 가 끝나더라도 다른 thread 는 실행을 계속할 수 있음.
main thread 가 끝났다고 해서 program 이 끝나는 것이 아니라, 모든 thread 가 끝나야만 완료되는 것임.
Multithread programming은 아래 두 가지 방법으로 작성할 수 있다.
java.lang.Thread 클래스를 이용
java.lang.Runnable 인터페이스를 이용
Multithread program - java.lang.Thread
A~Z까지 출력하는 thread와 0~9까지 출력하는 thread를 만들어 실행시켜보자
Thread 클래스를 상속받아 0~9를 차례로 출력하는 DigitThread를 생성하고 main에서 DigitThread를 start
public class DigitThread extends Thread {
public void run(){ //Thread의 run 메소드 오버라이딩
for (int cnt =0; cnt<10; cnt++) {
System.out.print(cnt);
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class MultiThreadExam01 {
public static void main(String[] args) {
Thread th = new DigitThread(); //다형성
th.start();
for(char ch = 'A'; ch <= 'Z'; ch++) { //main Thread
System.out.print(ch);
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
숫자와 알파벳이 패턴없이 섞여서 출력된다. -> 동시에 실행 중이라는 뜻
실제로 thread 가 동시에 실행되는 것은 아니다. 실행 시간을 아주 작은 간격으로 나누어 thread 들을 번갈아 실행할 뿐이며 thread에 할당하는 시간과 실행 순서는 하드웨어의 성능과 시스템 환경에 따라 달라질 수 있다.
Multithread program - java.lang.Runnable
ㄱ~ㄴ 까지 출력하는 thread와 a~z 까지 출력하는 thread를 만들어 실행시켜보자
Runnable 클래스를 구현해 a~z를 차례로 출력하는 SmallLetters를 생성하고 main에서 SmallLetters를 start
public class SmallLetters implements Runnable{
//Runnable은 인터페이스 이므로 implements , 자바는 다중상속이 안되기 때문에 Runnable을 사용해야만 하는 경우가 있음
public void run() {
for(char ch='a'; ch<'z'; ch++) {
System.out.print(ch);
}
}
}
public class MultiThreadExam03 {
public static void main(String[] args) {
Thread th = new Thread( new SmallLetters() );
th.start();
char arr[] = {'ㄱ','ㄴ','ㄷ','ㄹ','ㅁ','ㅂ','ㅅ','ㅇ','ㅈ','ㅊ','ㅋ','ㅌ','ㅍ','ㅎ'};
for(char ch : arr) {
System.out.print(ch);
}
}
}
자바는 다중상속을 허용하고 있지 않기 때문에 다중 상속을 피해야 할 경우 Runnable을 구현하는 방식으로 사용해야 한다.
공유 영역
thread들이 서로 무관하게 실행되기도 하지만 경우에 따라 서로 데이터를 교환해야 할 경우도 있다. 즉 두 thread 간의 communication이 필요할 때가 있다.
두 thread가 데이터를 교환하는 기본적인 방법은 두 thread 모두 접근 가능한 메모리 영역을 만들어 둔 후, 한 쪽 thread는 그 영역에 데이터를 쓰고, 다른 thread는 데이터를 읽는 방식이다.
원주율 계산 Multithread program
원주율을 계산하는 thread와 그 결과값을 출력하는 thread를 만들어 보자
public class SharedArea {
double result;
boolean isReady;
}
아래에서 작성할 CalcThread와 PtrThread가 공유하는 영역 SharedArea를 생성
공유 영역은 레퍼런스 타입으로 선언해야 여러 thread 가 참조값을 가지고 접근할 수 있다.
result에는 CalcThread가 생성한 결과값을 저장
isReady에는 CalcThread가 공유 데이터 result를 작성 후 isReady 값을 True로 바꿔 PtrThread에게 알려준다.
public class CalcThread extends Thread{
SharedArea sA;
public void run(){
double total = 0.0;
for (int cnt=1; cnt<10000; cnt+= 2) {
if (cnt/2%2 == 0)
total += 1.0/cnt;
else
total -= 1.0/cnt;
}
sA.isReady = true;
sA.result = total * 4;
synchronized (sA) {
sA.notify();
}
}
}
CalcThread 가 공유 영역에 데이터를 쓰고 난 다음에, isReady의 필드 값을 true 로 설정하면,
PrtThread 는 그 값을 가지고 공유 데이터의 유무를 체크할 수 있다.
public class PrtThread extends Thread{
SharedArea sA;
public void run() {
// while (sA.isReady != true)
// continue;
if(sA.isReady != true) {
try {
synchronized (sA) {
sA.wait();
}
}catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(sA.result);
}
}
PtrThread는 공유 영역의 isReady 값이 true가 될 때까지 무한히 루프를 돌아야 한다 -> 매우 비효율적임
따라서 CalcThread가 값을 입력하고 PtrThread에게 직접 신호를 보내자 (synchronization)
critical section
- thread 실행 중에 다른 thread 로 제어가 넘어가면 문제를 일으킬 수 있는 부분
- 주로 공유 데이터를 사용하는 부분
critical section의 synchronization
- critical section 이 실행되는 동안 다른 thread 가 공유 데이터를 사용할 수 없도록 만드는 것
public class MultiThreadExam04 {
public static void main(String[] args) {
CalcThread th1 = new CalcThread();
PrtThread th2 = new PrtThread();
SharedArea obj = new SharedArea();
th1.sA = obj;
th2.sA = obj;
th1.start();
th2.start();
}
}