프로세스와 쓰레드
프로세스
실행 중인 프로그램
프로그램이 실행하면 OS에서 실행에 필요한 메모리를 할당받아 프로세스가 된다.
메모리
- 데이터
- 메모리 등의 자원
- 1개 이상의 쓰레드
기존 DOS OS에서는 하나의 작업만 가능했지만, 최근 대부분의 OS는 멀티태스킹을 지원합니다. 따라서 여러 프로세스를 실행시킬 수 있습니다.
쓰레드
하나의 프로세스에서는 여러 쓰레드를 할당받을 수 있다.
- 갯수에 제한은 없다
- 단, 메모리에서 호출스택에 제한되기 대문에 생성할 수 있는 쓰레드 수가 한정된다.
멀티태스킹
- 하나의 CPU코어는 하나의 작업만 처리 가능
- CPU코어의 갯수보다 쓰레드의 갯수가 많기 때문에 여러 작업을 번갈아 실행
장점:
- CPU의 사용률을 향상
- 자원을 보다 효율적 사용
- 사용자에 대한 응답성 향상
- 작업이 분리되어 코드 간결
단점:
- 동기화 ( Synchronization )
- 교착상태 ( deadlock )
쓰레드 구현
쓰레드를 구현하는 방법은 Thread클래스를 상속받던지, Runnable 인터페이스를 구현하던지 2가지 방법이 있습니다.
Thread 클래스
public class CustomThread extends Thread {
@Override
public void run(){
// override해도 되고, 안해도 된다.
}
}
// main
CustomThread ct = new CustomThread();
ct.start();
이렇게 한다면, 다른 클래스를 상속받을 수 없기 때문에 제한적이다.
Runnable 인터페이스
public class CustomThreadAble implements Runnable{
@Override
public void run() {
// 반드시 오버라이드 해야함
}
}
// main
Runnable customThreadAble = new CustomThreadAble();
Thread thread = new Thread(customThreadAble);
thread.start();
// or
Thread th = new Thread(new CustomThreadAble());
th.start();
- 재사용성(Reusuability)가 높다
- 코드의 일관성(Consistency)를 유지할 수 있기 때문에 객체지향적인 방법이다.
쓰레드 이름
getName(); // Thread 클래스를 상속받는다면 getName에 대한 메서드가 존재하고, 해당 메서드로 가져올 수 있다.
Thread.currentThread().getName(); // Runnable
Runnable는 인터페이스는 getName에 대한 구현체가 없습니다. 따라서 Thread 클래스의 static Thread currentThread()를 통해 현재 쓰레드를 가져와야 한다.
Thread start() vs run()
start()
- 새로운 쓰레드를 하나 추가한다는 것이다.
- 새로운 호출스택을 만들어 낸다.
run()
- 새로 생성된 호출스택에 해당 쓰레드가 실행한 run메서드를 올린다.
- run메서드가 끝나는것과 같이 해당 호출스택은 사라진다.

이러한 모든 쓰레드가 동작을 완료해야 프로그램을 종료할 수 있다.
동작 코드에 따른 결과
@Override
public void run(){
error();
}
private void error() {
try {
throw new Exception();
} catch (Exception e) {
e.printStackTrace();
}
}
// main
CustomThread customThread = new CustomThread();
customThread.start(); // # Thread1
System.out.println(customThread.getThreadGroup()); // java.lang.ThreadGroup[name=main,maxpri=10]
Runnable customThreadAble = new CustomThreadAble();
Thread thread = new Thread(customThreadAble);
Thread thread2 = new Thread(new CustomThreadAble());
thread.setPriority(1);
thread.start(); // # Thread2
thread2.setPriority(3);
thread2.start(); // # Thread3
thread = new Thread(customThreadAble);
thread.start(); // # Thread4

위 결과를 보면 main 쓰레드에서 thread를 4개를 실행 시켰고 각 쓰레드에서 예외를 발생하게 했습니다.
따라서 4개의 예외가 나온것을 볼 수 있습니다. 여기서 main 쓰레드는 이미 종료된 것을 알 수 있는데
Thread1에서 start()를 통해 쓰레드를 추가하여 호출스택에 run()메서드를 올리는 것이 아닌, run을 바로 실행하게 되면

위와 같이 main 메서드에서 예외처리가 출력된 것을 볼 수 있습니다.
동일한 쓰레드 생성시 오류
CustomThread customThread = new CustomThread();
try {
customThread.start();
customThread.start();
} catch (Exception e) {
e.printStackTrace();
}
위와 같이 동일한 쓰레드에 대해서 실행하게 되면 IllegalThreadStateException에러가 발생한다.

Single Thread vs Multi Thread
그럼 무조건 쓰레드가 많을 수록 좋은 것인가? 꼭 그렇지만은 않다
쓰레드의 스위칭 비용
- 쓰레드 또한 프로세스 처럼 스위칭 비용이 든다.
JVM 쓰레드 스케쥴링
- JVM에도 쓰레드 스케쥴러가 있어서 실행순서와 실행 시간이 다르다.
- 따라사 항상 동일한 성능을 내지 않기 때문에 배포하려는 환경에서의 JVM에서 스케쥴링을 참고하는 것이 좋다.
구분
- Single Thread: 계산과 같은 하나의 동작을 하는 경우 싱글 쓰레드가 더 높은 성능을 낸다. (NodeJs)
- Multi Thread: 사용자 입력과 같이 서로 다른 자원을 접근하여 사용하는 경우 더 효율적이다. (Spring)
Thread Group
쓰레드를 그룹으로 묶어서 관리할 수 있고, 이를 통해 쓰레드들의 생성, 실행, 정지, 종료 등을 통합적으로 관리할 수 있다.
그룹:
- System Thread Group : 기본적인 모든 쓰레드 (Ex, 가비지컬렉션을 수행하는 Finalizer)
- Main Thread Group : main 메서드를 실행하는 그룹
데몬 쓰레드 (Daemon Thread)
일반 쓰레드와 달리 메인 쓰레드가 종료되면 같이 종료되는 쓰레드를 말합니다. 보통 백그라운드 작업을 수행하는데 사용합니다.
백그라운드 작업
- 가비지 컬렉터
- 화면 자동 갱신
- 운영체제에서 정기적으로 수행되는 작업
- 네트워크 입출력
데몬쓰레드는 무한루프와 조건문을 이용하여 동작한다.
'JAVA > 자바스터디' 카테고리의 다른 글
[자바스터디] 쓰레드 동작 순서 (0) | 2023.01.29 |
---|---|
[자바스터디] Generic vs Object (0) | 2023.01.28 |
[자바스터디] Generic (0) | 2023.01.28 |
[자바 스터디] for vs foreach vs iterator (0) | 2023.01.28 |
[자바스터디] Collection - Collections (0) | 2023.01.22 |