🤔 Thread 공부하기 전에 프로세스부터 알고 넘어가기
- 운영체제의 관점에서 실행 중인 독립적인 프로그램이다.
- 메모장, 크롬, 이클립스를 실행했다면?
- 3개의 어플리케이션을 실행한 상태이며, 각 어플리케이션은 별도의 독립적인 프로세스가 존재하기 때문에 멀티 태스킹이 가능하다.
- 자원과 스레드로 구성되어 있다.
🤷♀️ Thread란?
- 명령어들의 집합이다.
- 프로세스 내에서 실제 작업을 수행하며, 모든 프로세스는 최소 1개 이상의 스레드를 가진다.
- 대부분 이를 메인 스레드라고 하며, 부가적인 스레드를 생성하여 병렬 작업을 수행할 수 있다.
- 자원을 효율적으로 사용할 수 있다.
- 여러 스레드가 동시에 실행되어 CPU 시간 및 메모리 등 자원을 효과적으로 활용할 수 있다.
- 사용자에 대한 응답성이 향상된다.
- 하나의 스레드가 블록되어 작업을 수행하는 동안에도 다른 스레드들이 실행되므로 전체적으로 응답성이 향상된다.
- 작업이 분리되어 코드가 간결해진다.
- 각 스레드가 특정 작업에 집중할 수 있어 코드가 간결해지고 유지보수가 쉬워진다.
- 동기화(synchronization)에 주의해야 한다.
- 자원과 2개의 A 스레드, B 스레드가 있을 때
- CPU → A 스레드에 시간을 할당하고, 주어진 시간이 끝나면 중간에 멈췄다가,
B 스레드에 시간을 할당하고, 주어진 시간이 끝나면 다시 A 스레드로... 왔다리갔다리 한다.- 이때, CPU 속도가 빠르기 때문에 동시에 실행되는 것처럼 보인다.
⛔⛔ 처리 도중 자원의 데이터가 변경되면 A 스레드와 B 스레드는 다른 데이터를 처리하게 된다.- 교착 상태에 주의해야 한다.
- 교착 상태란, 둘 이상의 프로세스나 스레드가 서로 상대방의 작업이 끝나기를 기다리며 무한히 대기하는 상태다.
- 각 스레드가 효율적으로, 고르게 실행될 수 있게 해야 한다.
- Thread 클래스를 상속한 class를 작성한 후, 인스턴스 생성
run()메소드를 재정의하여 코드 작성
- 재정의한 내용이 스레드를 처리할 때 실행
- 인스턴스의
start()메소드를 호출하여 실행
❗java는 단일 상속만을 지원하기 때문에, 이미 다른 클래스를 상속받고 있을 때에는 이 방법을 사용할 수 없다.
- Runnable 인터페이스를 구현한 class를 작성한 후, 인스턴스 생성
run()메소드를 재정의하여 코드 작성
- 재정의한 내용이 스레드를 처리할 때 실행
- 인스턴스를 Thread 클래스의 인스턴스를 생성할 때 생성자의 인수값으로 넘겨줌
- 인스턴스의
start()메소드를 호출하여 실행
❗ Runnable 인터페이스를 구현할 경우, 단일 상속과는 상관없이 멀티 스레드를 만들 수 있다.
👉 인터페이스는 하나의 클래스가 여러개를 구현할 수 있기 때문에!
package kr.or.ddit.basic;
public class ThreadTest14 {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
thread1.start();
MyRunner runner = new MyRunner();
Thread thread2 = new Thread(runner);
thread2.start();
}
}
// 1. Thread 클래스 상속
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("*");
try {
Thread.sleep(1000); // 단위는 밀리세컨드 (1초 -> 1000)
} catch (InterruptedException e) {
}
}
}
}
// 2. Runnable 인터페이스 구현
class MyRunner implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("#");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
}
😶 Thread.sleep()을 try-catch문으로 처리하는 이유
sleep()메소드는 스레드를 지정된 시간동안 일시중지 시키는데, 중간에 다른 스레드가 일시중지 된 스레드를interrupt()메소드를 통해 깨우려고 할 때InterruptedException이 발생할 수 있다.- 따라서 코드의 안정성과 적절한 대응을 하기 위해 예외 처리를 해주는 것이 좋다.
run() 메소드를 재정의했는데 start() 메소드를 호출하는 이유start() 메소드를 호출하면 새로운 스레드가 생성되고, 내부적으로 해당 스레드가 실행되는데, 그 안에서 run() 메소드가 호출되기 때문이다. 직접적으로 run() 메소드를 실행할 경우 일반적인 단순 메소드 호출이 되기 때문에 single thread와 동일하게 처리된다. main() 메소드의 실행과는 병렬적으로 동작되어 main() 메소드가 실행되는 동안 다른 스레드를 생성하고 실행할 수 있다.
- 일반 스레드의 보조 역할이며, 일반 스레드가 모두 종료되면 자동으로 종료된다.
- 일반 스레드와 만드는 방법은 동일함
- 스레드 실행 전 (
start()호출 전) 데몬 스레드라고 명시해야 하고,start()이후 설정시 Exception 발생
setDaemon()→ true: 데몬스레드, false: 일반스레드- 데몬 스레드 확인
isDaemon()→ true: 데몬스레드, false: 일반스레드
| 상태 | 설명 |
|---|---|
| NEW | 스레드 객체 생성 후 start() 호출 전 |
| RUNNABLE | 실행 중 / 실행 가능한 상태 |
| BLOCKED, WAITING, TIMED_WAITING | 일시정지된 상태 (sleep(), join(), suspend(), wait(), I/O block), 주어진 시간이 끝나면 일시정지를 마치고 대기열로 감 (resume(), notify(), interrupt()) |
| TERMINATED | 스레드 작업 종료 및 스레드 객체 소멸 |

suspend(): 스레드 일시 정지
resume(): suspend()에 의해 일시 정지 상태인 스레드를 실행 대기 상태로
join(): 지정된 시간동안 스레드 실행, 지정된 시간이 지나거나 작업 종료시 join()을 호출한 스레드로 돌아가서 실행
interrupt(): sleep(), join()에 의해 일시 정지된 스레드를 실행 대기 상태로, InterruptException 발생시 일시 정지 상태를 벗어남
sleep(): 지정된 시간동안 스레드 일시 정지, 지정된 시간이 지나면 자동으로 실행 대기 상태
stop()
yield(): 실행 중 다른 스레드에게 양보 후 실행 대기 상태로public void setSd(ShareData sd) {
this.sd = sd;
}
public PrintPIThread(ShareData sd) {
this.sd = sd;
}
Vector, Hashtable 등과 같이 예전부터 존재하던 Collection 객체들은 내부에 동기화 처리가 되어 있다. 새로 구성된 Collection들은 동기화 처리가 되어 있지 않다. 동기화가 필요한 프로그램에서 이런 Collection들을 사용하려면 동기화 처리 후 사용해야 한다.
