프로그램을 실행하기 위해 필요한 모든 자원을(메모리 등), OS로부터 할당받은 뒤 추상화한 것(?)
한 번에 여러 프로그램을 실행할 수 있음 = 멀티태스킹 = 멀티 프로세스
프로세스 내부에서 실질적으로 작업을 수행하는 주체
하나의 프로세스는 하나 이상의 쓰레드로 구성됨
aka. light weight process (경량 프로세스)
Slack을 예로 들면
이는 여러 Thread가 각각의 역할(알림, 게시물 업데이트, 글 작성)을 수행하기 때문이다.
IntelliJ Process에서 85개의 Thread가 각자의 역할을 수행하는 중이다.
쓰레드를 생성하는 작업이, 프로세스를 생성하는 작업보다 비용이 적게 든다.
쓰레드를 전환하는 작업이, 프로세스를 전환하는 작업보다 비용이 적게 든다. (Context Switching)
각 쓰레드는 자신만의 고유한 Stack 영역을 가지고, 나머지 영역(Code, heap, Data)은 공유한다. 이러한 특성 덕분에, 프로세스보다 효율적인 Context Switching이 가능하다는 장점이 있다.
그렇다고 멀티 쓰레드 프로그램이 항상 좋은건 만은 아니다.
여러 쓰레드가 자원을 공유하기 때문에
또한 여러 Thread들이 어떤 순서로 실행되어야 할지를 고려해야한다. (starvation)
자바는 두 가지 방법의 쓰레드 구현 방법이 있다.
A Thread클래스는 프로그램을 실행하는 thread이다.JVM은 app이 동시에 실행될 수 있는 멀티 스레드를 지원한다.
모든 쓰레드는 우선순위가 있다. 높은 우선순위를 가진 쓰레드는 상대적으로 낮은 우선순위를 가진 쓰레드보다 먼저 실행된다.
각 쓰레드는 deamon 쓰레드로 마킹될 수도 있다.
특정 쓰레드에서 실행중인 코드에서 새로운 쓰레드 객체를 만들면, 새로 생성된 쓰레드는 자신을 생성한 쓰레드와 동일한 우선순위를 갖는다.
또한 자신을 생성한 쓰레드가 deamon이면, 새로 생성된 쓰레드도 deamon이다.
JVM이 시작되면(일반적으로 메인 메소드를 호출한 경우), 보통은 Single&non-daemon 쓰레이드이다.
JVM은 다음 조건을 만날 때 까지 계속 쓰레드를 실행시킨다.
exit
메소드가 호출되고, security manager
가 종료 작업 수행을 허용한 경우public class Thread implements Runnable {
...
}
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("this is my Thread");
}
}
public class Main {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start(); // this is my Thread
}
}
Runnable
인터페이스는 클래스의 인스턴스가 쓰레드에서 실행되도록 의도된 클래스에의해 구현되어야 한다.
구현 클래스는 반드시 파라미터가 없는 run
이라는 이름의 메소드를 override해야한다.
활성화된 상태에서 코드를 실행시키고 싶은 객체들을 위해 일반적인 프로토콜을 제공하기 위해 사용됨
예를 들어, Runnable
은 Thread
클래스에서 구현된다.
활성화된 상태는 단순히 쓰레드가 실행되고 아직 정지되지 않은 상태를 의미한다.
Runnable
은 Thread
를 상속받지 않고도 클래스가 active할수 있는 방법을 제공한다.
Runnable
은 Thread
의 메소드들 중, run
만을 override할 때 사용한다.
public class YourThread implements Runnable {
@Override
public void run() {
System.out.println("this is your Thread");
}
}
public class Main {
public static void main(String[] args) {
Thread yourThread = new Thread(new YourThread());
yourThread.start(); // this is your Thread
}
}
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("this is my Thread " + i);
}
}
}
public class YourThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("this is your Thread " + i);
}
}
}
public class Main {
public static void main(String[] args) {
Thread myThread = new MyThread();
myThread.start();
Thread yourThread = new Thread(new YourThread());
yourThread.start();
}
}
싱글쓰레드의 경우 myThread의 start작업이 종료되어야, youtThread의 start작업이 실행된다.
멀티 쓰레드는 그렇지 않다.
쓰레드는 State
enum을 통해 state를 표현(관리)한다.
Status | Description |
---|---|
NEW | 쓰레드가 생성되었으나, 아직 시작되지 않은 상태 |
RUNNABLE | JVM에서 실행중인 상태 |
BLOCKED | monitor lock 얻을때 까지 block된 thread 상태 |
WAITING | 기다리는 상태 |
TIMED_WAITING | 특정한 시간동안 WAITING인 상태 |
TERMINATED | 실행을 완료한 상태 |
JVM에서 실행중이나, 프로세서와 같은 리소스를 획득하기 위해 기다리는 중일 수도 있다.
blocked
state의 쓰레드는 synchronized block(영역) 또는 메소드에 진입하기 위해, 또는 Object.wait
을 호출한 뒤, 다시 synchronized block/method에 진입하기 위해 monitor lock
을 기다린다.
다음의 상황에서 쓰레드가 WAITING
상태가 될 수 있다.
Object.wait
with no timeoutThread.join
with no timeoutLockSupport.park
WAITING
상태의 쓰레드는 다른 쓰레드에서 특정 행동을 하길 기다린다.
예를 들어
Object.wait
를 호출한 쓰레드 객체는, 다른 쓰레드에서 Object.notify
또는 Object.notifyAll
를 호출하길 기다린다. 다음의 상황에서 쓰레드가 TIMED_WAITING
상태가 될 수 있다.
Thread.
sleep`Object.wait
with timeoutThread.join
with timeoutLockSupport.parkNanos
LockSupport.parkUntil