프로세스
- 실행 중인 프로그램
- 자원(resource)과 스레드로 구성
스레드
- 프로세스 내에서 실제 작업을 수행
- 모든 프로세스는 최소한 하나의 스레드를 가지고 있다.
프로세스가 공장이면, 스레드는 일꾼이다.
스레드가 하나이면 싱글 스레드 프로세스, 스레드가 여러 개면 멀티 스레드 프로세스라고 한다.
대부분의 프로그램은 멀티 스레드이다.
PID : 프로세스 식별자, 프로세스 id
상태 : 현재 프로그램이 실행 중인지 일시 중단인지 등의 상태를 나타낸다.
하나의 새로운 프로세스를 생성하는 것보다 하나의 새로운 스레드를 생성하는 것이 더 적은 비용이 든다.
멀티 스레드의 장단점
장점 | - 시스템 자원을 보다 효율적으로 사용할 수 있다. - 사용자에 대한 응답성이 향상된다. - 작업이 분리되어 코드가 간결해 진다. |
단점 | - 동기화에 주의해야 한다. - 교착상태가 발생하지 않도록 주의해야 한다. - 각 스레드가 효율적으로 고르게 실행될 수 있게 해야 한다. |
스레드의 구현과 실행
스레드 구현방법으로는 아래와 같이 두 가지 방법이 있다. 이중 Runnable 인터페이스로 구현하는 것이 보다 좋다. 자바의 경우 클래스는 단일 상속만 가능하지만 인터페이스는 다른 클래스도 추가로 상속받을 수 있기 때문이다.
1. Thread 클래스를 상속
class MyThread extends Thread {
public void run() { // Thread클래스의 run()을 오버라이딩
// 작업내용
}
}
MyThread t = new MyThread(); // 스레드의 생성
t.start(); // 스레드의 실행
2. Runnable 인터페이스를 구현
class MyThread implements Runnable {
public void run() { // Runnable 인터페이스의 추상메서드 run()을 구현
// 작업내용
}
}
Runnable r = new MyThread();
Thread t = new Thread(r); // Thread(Runnable r)
// Thread t = new Thread(new MyThread());
t2.start();
Thread 클래스를 상속받으면, 자손 클래스에서 조상인 Thread 클래스의 메서드를 직접 호출할 수 있지만,
Runnable을 구현하면 Thread 클래스의 static 메서드인 currentThread()를 호출하여 스레드에 대한 참조를 얻어 와야만 호출이 가능하다.
static Thread currentThread() 현재 실행중인 스레드의 참조를 반환한다.
String getName() 스레드의 이름을 반환한다.
스레드의 이름은 다음과 같은 생성자나 메서드를 통해서 지정 또는 변경할 수 있다. 스레드의 이름을 지정하지 않으면 'Thread-번호'의 형식으로 정해진다.
Thread(Runnable target, String name)
Thread(String name)
void setName(String name)
class ThreadEx01 {
public static void main(String args[]) {
ThreadEx1_1 t1 = new ThreadEx1_1();
Runnable r = new ThreadEx1_2();
Thread t2 = new Thread(r); // 생성자 Thread(Runnable target)
t1.start();
t2.start();
/*
스레드를 사용하지 않았다면 위 코드 대신 아래처럼 작성했을 것이다.(싱글스레드)
for(int i=0; i < 5; i++) {
System.out.println(0);
}
for(int i=0; i < 5; i++) {
System.out.println(1);
}
*/
}
}
class ThreadEx1_1 extends Thread { // Thread클래스를 상속해서 스레드를 구현
public void run() { // 스레드가 수행할 작업을 작성
for(int i=0; i < 5; i++) {
System.out.println(this.getName()); // 조상인 Thread의 getName()을 호출
}
}
}
class ThreadEx1_2 implements Runnable { //Runnable인터페이스를 상속해서 스레드를 구현
public void run() { // 스레드가 수행할 작업을 작성
for(int i=0; i < 5; i++) {
// Thread.currentThread() - 현재 실행중인 Thread를 반환한다.
System.out.println(Thread.currentThread().getName());
}
}
}
반복문에서 출력되는 개수를 늘리면 t1과 t2 작업의 결과가 번갈아 가면서 나온다.
스레드를 생성한 후 start()를 호출해야 스레드가 작업을 시작한다.
ThreadEx1_1 t1 = new ThreadEx1_1(); // 스레드 t1을 생성한다.
ThreadEx1_1 t2 = new ThreadEx1_1(); // 스레드 t2를 생성한다.
t1.start(); // 스레드 t1을 실행시킨다.
t2.start(); // 스레드 t2를 실행시킨다.
t1.start()가 t2.start() 보다 위에 있어서 먼저 실행될 확률이 높지만, 항상 t1이 t2보다 먼저 실행되는 것은 아니다. 스레드를 start 하면 실행 가능한 상태가 되는 것이지 바로 실행되는 것은 아니다.
왜냐하면 OS의 스케줄러가 실행 순서를 결정하기 때문이다.
한 번 실행이 종료된 스레드는 다시 실행할 수 없다. 하나의 스레드에 대해 start()가 한 번만 호출될 수 있다.
만일 스레드의 작업을 한 번 더 수행해야 한다면 새로운 스레드를 생성한 다음 start()를 호출해야 한다.
하나의 스레드에 대해 start()를 두 번 이상 호출하면 IllegalTrheadStateException이 발생한다.
ThreadEx1_1 t1 = new ThreadEx1_1();
t1.start();
t1 = new ThreadEx1_1(); // 다시 생성
t1.start();
start()와 run()
main()에서 t1.start()를 호출하면 그림 1 상태에서 start()로 인해 그림 2와 같이 새로운 호출 스택을 생성한다. 그리고 그림 3과 같이 새로 생성된 호출 스택에 run을 올린다. 그러면 기존 호출 스택에 있던 start가 할 일을 마쳤기 때문에 종료가 되어 그림 4와 같이 된다.
따라서 서로 독립적인 작업이 가능해진다.
main()에서 start()가 아닌 run()을 호출하면 멀티 스레드가 아닌 이전과 같이 싱글 스레드로 작업하게 된다. 그래서 run()이 아닌 start()를 호출하는 것이다.
'JAVA' 카테고리의 다른 글
[Thread] sleep(), interrupt()와 interrupted() (0) | 2023.02.26 |
---|---|
[Thread] 데몬 스레드, 스레드의 상태 (0) | 2023.02.26 |
[Thread] 스레드의 우선순위 (0) | 2023.02.26 |
[Thread] 싱글 스레드와 멀티 스레드, 스레드의 I/O 블로킹 (0) | 2023.02.26 |
[Collections Framework] Comparator, Comparable (0) | 2023.02.24 |