본문
싱글쓰레드와 멀티쓰레드
# 싱글쓰레드와 멀티쓰레드
두 개의 작업을 하나의 쓰레드로 처리하는 경우와 두 개의 쓰레드로 처리하는 경우를 가정해보자.
하나의 쓰레드로 두 작업을 처리하는 경우는 한 작업을 마친 후에 다른 작업을 시작하지만, 두 개의 쓰레드로 작업 하는 경우에는 짧은 시간동안 2개의 쓰레드가 번갈아 가면서 작업을 수행해서 동시에 두 작업이 처리되는 것과 같이 느끼게 한다. 하지만 오히려 두 개의 쓰레드로 작업한 시간이 싱글쓰레드로 작업한 시간보다 더 걸릴 수 도 있는데 그 이유는 쓰레드간의 작업전환(context switching)에 시간이 걸리기 때문이다. 그래서 단순히 CPU만을 사용하는 계산작업이라면 오히려 멀티쓰레드보다 싱글쓰레드로 프로그래밍하는 것이 더 효율적이다. (참고 : 프로세스간 또는 쓰레드간의 전환을 '컨텍스트 스위칭(context switching)이라고 한다.)
작업전환할 때는 현재 진행중인 작업의 상태, 예를 들면 다음에 실행해야할 위치(PC프로그램 카운터) 등의 정보를 저장하고 읽어 오는 시간이 소요된다. 참고로 쓰레드의 스위칭에 비해 프로세스의 스위칭이 더 많은 정보를 저장해야하므로 더 많은 시간이 소요된다.
'-'를 출력하는 작업과 '|'를 출력하는 작업을 하나의 쓰레드가 연속적으로 처리하는 시간을 측정하는 예제이다.
public class ThreadEx02 {
final static int LOOP = 300;
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
for (int i = 0; i < LOOP; i++) {
System.out.print("-");
}
System.out.println("소요시간1:" + (System.currentTimeMillis() - startTime));
for (int i = 0; i < LOOP; i++) {
System.out.print("|");
}
System.out.println("소요시간2:" + (System.currentTimeMillis() - startTime));
}
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------소요시간1:3
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||소요시간2:4
이제 새로운 쓰레드를 하나 생성해서 두 개의 쓰레드가 작업을 하나씩 나누어서 수행한 후 실행결과를 비교하도록 하자.
public class ThreadEx03 {
final static int LOOP = 300;
// static long startTime = 0;
static long startTime = System.currentTimeMillis();
public static void main(String[] args) {
ThreadEx thread = new ThreadEx();
thread.start();
for (int i = 0; i < LOOP; i++) {
System.out.print("-");
}
System.out.print("소요시간1 : " + (System.currentTimeMillis() - startTime));
}
}
class ThreadEx extends Thread {
final static int LOOP = 300;
static long startTime = System.currentTimeMillis();
public void run() {
for (int i = 0; i < 300; i++) {
System.out.print("|");
}
System.out.print("소요시간2 : " + (System.currentTimeMillis() - startTime));
}
}
------------------------------------------------------------||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||----------------------소요시간2 : 3--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------소요시간1 : 6
이전 예제와는 달리 두 작업이 아주 짧은 시간동안 번갈아 가면서 실행되었으며 거의 동시에 작업이 완료되었음을 알 수 있다. 위의 결과는 실행할 때마다 다른 결과를 얻을 수 있는데 그 이유는 실행 중인 예제프로그램(프로세스)이 OS의 프로세스 스케줄러의 영향을 받기 때문이다. 자바가 OS(플랫폼) 독립적이라고 하지만 실제로는 OS종속적인 부분이 몇 가지 있는데 쓰레드도 그 중 하나이다.
CPU 이외의 자원을 사용하는 작업의 경우에는 싱글쓰레드 프로세스보다 멀쓰레드 프로세스가 더 효율적이다. 예를 들면 사용자로부터 데이터를 입력받는 작업, 네트워크로 파일을 주고 받는 작업, 프린터로 파일을 출력하는 작업과 같이 외부기기와의 입출력을 필요로하는 경우가 이에 해당된다.
만일 사용자로 부터 입력받는 작업(work1)과 화면에 출력하는 작업(work2)을 하나의 쓰레드로 처리한다면 사용자가 입력을 마칠 때 까지 아무일도 하지 못하고 기다리기만 해야한다. 그러나 두 개의 쓰레드로 처리한다면 사용자의 입력을 기다리는 동안 다른 쓰레드가 작업을 처리할 수 있기 때문에 보다 효율적인 CPU의 사용이 가능하다.
다음 예제는 하나의 쓰레드로 사용자의 입력을 받는 작업과 화면에 숫자를 출력하는 작업을 처리하기 때문에 사용자가 입력을 마치기 전까지는 화면에 숫자가 출력되지 않다가 사용자가 입력을 마치고나서야 화면에 숫자가 출력된다.
import javax.swing.JOptionPane;
public class ThreadEx04 {
final static int LIMITED_TIME = 10;
public static void main(String[] args) {
String input = JOptionPane.showInputDialog("아무값이나 입력하세요.");
System.out.println("입력하신 값은 " + input + " 입니다.");
for (int i = LIMITED_TIME; i > 0; i--) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (Exception e) {
}
}
}
}
입력하신 값은 7 입니다.
10
9
8
7
6
5
4
3
2
1
이전 예제와는 달리 사용자로부터 입력받은 부분과 화면에 숫자를 출력하는 부분을 두 개의 쓰레드로 나누어서 처리했기 때문에 사용자가 입력을 마치지 않았어도 화면에 숫자가 출력되는 것을 알 수 있다.
import javax.swing.JOptionPane;
public class ThreadEx04_1 {
public static void main(String[] args) {
Thread_Ex thread01 = new Thread_Ex();
thread01.start();
String input = JOptionPane.showInputDialog("아무값이나 입력하세요.");
System.out.println("입력하신 값은 " + input + "입니다.");
}
}
class Thread_Ex extends Thread {
final static int LIMMITED_TIME = 10;
public void run() {
for (int i = LIMMITED_TIME; i > 0; i--) {
System.out.println(i);
try {
sleep(1000);
} catch (Exception e) {
}
}
}
}
10
9
8
입력하신 값은 3입니다.
7
6
5
4
3
2
1
10초 동안 사용자가 입력하지 않으면 종료하는 예제이다.
import javax.swing.JOptionPane;
public class ThreadEx05 {
static boolean inputCheck = false;
public static void main(String[] args) throws Exception {
Thread01 thread01 = new Thread01();
Thread02 thread02 = new Thread02();
thread01.start();
thread02.start();
}
}
class Thread01 extends Thread {
public void run() {
System.out.println("10초 안에 값을 입력해야 합니다.");
String input = JOptionPane.showInputDialog("아무값이나 입력하세요.");
ThreadEx05.inputCheck = true;
System.out.println("입력값은 : " + input + "입니다.");
}
}
class Thread02 extends Thread {
final static int LIMITED_TIME = 9;
public void run() {
for (int i = LIMITED_TIME; i >= 0; i--) {
System.out.println(i);
try {
sleep(1000);
} catch (InterruptedException ie) {
}
}
if (ThreadEx05.inputCheck != true) {
System.out.println("10초 동안 값이 입력되지 않아 종료합니다.");
System.exit(0);
}
}
}
10초 안에 값을 입력해야 합니다.
9
8
7
6
5
4
3
2
1
0
10초 동안 값이 입력되지 않아 종료합니다.
- 참고
JAVA의정석(남궁성 저)
댓글