💡 해당 글은 『자바의 신 3판』을 복습하며 도서의 내용과 본인의 주관적인 생각을 정리한 글입니다.
☁️ 내용정리
Thread
Thread란 프로세스 내에서 독립적으로 작업을 수행하는 실행 단위이다.
스레드가 왜 만들어졌을까? 만약 모든 프로세스가 하나의 작업을 수행할 수 있었다면 어땠을까? 프로세스는 많은 자원을 소모하는 실행 단위이다. 만약 하나의 작업을 동시에 수행하려고 할 때, 스레드가 없다면 여러 개의 프로세스를 띄워 실행해야 할 것이다.
JVM은 기본적으로 아무런 옵션 없이 실행하면 OS마다 다르지만, 적어도 32 ~ 64MB의 물리 메모리를 점유한다. 그에 반해서, 쓰레드를 하나 추가하면 1MB 이내의 메모리를 점유한다. 그래서, 쓰레드를 “경량 프로세스”라고도 부른다.
- 자바의 신 2권 147p
Runnable과 Thread
스레드를 자바코드에서 로우레벨로 다루기 위해서는 Runnable
인터페이스를 구현하는 방법과 Thread
클래스를 상속 받는 방법 두 가지가 존재한다.
두 가지 방법 모두 run()
메서드에 스레드에서 동작할 코드를 명시적으로 작성하면 된다. 그리고 start()
를 통해 스레드를 실행시킬 수 있다.
스레드는 실행에 이써 우선순위가 있을 수 있다. 일반적으로 데몬스레드는 낮은 priority를 가진다.
실행 우선순위가 높다고 해서 우선순위가 낮은 스레드보다 무조건 빨리 실행되는 것은 아니다.
Synchronized
자바에서 thread-safe한 기능을 구현하기 위해서는 이 synchronized 예약어를 사용해야 한다.
synchronized로 선언된 메서드 혹은 블럭은 한번에 여러 스레드가 진입할 수 없고, 하나의 스레드만 진입할 수 있게 된다. 이러한 기능은 monitor라는 개념을 통해 지원된다.
스레드가 Critical Section에 진입하기 위해서는 lock(monitor)을 획득해야 한다. 먼저 진입한 스레드가 lock을 획득하고 Ciritcal Section의 자원을 사용하고 있는 상황에서 다른 스레드가 진입을 시도하는 경우 mutex의 Entry Queue에서 대기하게 된다.
이후 먼저 진입한 스레드가 lock을 반납하면 다음 스레드가 lock을 획득하며 Critical Section에 진입한다. 이때, 진입한 스레드가 작업을 수행할 수 있는 특정 조건을 만족하지 못하고 wait() 메서드가 호출된다면 Waiting Queue에서 대기하게 된다.
이때, Entry Queue에서 대기하고 있는 스레드의 상태는 Blocked
, 락 획득을 기다리는 상태이다.
Waiting Queue에서 대기하고 있는 스레드의 상태는 Waiting
, notify() 호출을 기다리는 상태이다.
sleep() 메서드를 호출한 스레드의 상태는 Timed_Waing
이다.
inerrupt() 메서드를 호출한 스레드의 상태는 Terminated
이다.
만약, 두 개의 메서드에 대해 각각의 독립적인 락을 원한다면, 독립적인 오브젝트 객체를 Synchronized 블럭에 넘겨주면 된다.
Thread Group
ThreadGroup는 스레드들을 논리적으로 그룹화하고 관리하기 위한 개념이다.
스레드 그룹은 트리 구조를 가진다. 즉, 하나의 그룹이 다른 그룹에 속할 수 도 있고, 그 아래에 또 다른 그룹을 포함할 수도 있다.
☁️ 내 생각
Thread
보통 자바로 간단한 프로그램을 하나 만들어 실행하면 이 프로그램은 싱글 스레드로 동작하게 된다.
하지만 JVM 시점에서는 좀 다르다. 자바 프로그램이 JVM 위에서 실행되며 가비지 컬렉션과 관련된 스레드 등도 함께 동작한다.
우리가 메인 메서드에서 스레드를 실행시킬 수 있는 방법은 두 가지가 있다.
java.lang
의 Runnable
인터페이스, Thread
클래스를 실행시키는 것이다.
@FunctionalInterface
public interface Runnable {
void run();
}
// 스레드 실행 방법
// case 1 : 람다를 통한 실행
new Thread(() -> System.out.println("start thread")).start();
// case 2 : Runnable 구현 클래스를 통한 실행
Runnable runnable = new RunnableImpl();
new Thread(runnable).start();
// case 3 : 익명 클래스 사용
Runnable runnable = new Runnable() {
@Override
public void run() {
// 로직
}
}
new Thread(runnable).start();
Runnable
은 run()
이라는 하나의 메서드를 가지는 함수형 인터페이스이다.
public class Thread implements Runnable {
// ...
}
// 스레드 실행 방법
Thread thread = new ThreadImpl();
thread.start();
Thread
는 Runnable
인터페이스를 구현하는 클래스이다.
일반 클래스도 결국 스레드 위에서 동작하지 않는가? 뭐가 다른지?
일반 클래스도 결국 스레드 위에서 동작한다.
하지만 스레드에 의해 호출되어 사용되며 스레드에서 수행할 동작을 명시적으로 선언할 수 없다.
class MyClass {
public void myMethod() {
//...
}
}
MyClass myclass = new MyClass();
Thread thread = new Thread(() -> myclass.myMethod());
thread.start();
Runnable
을 구현한 클래스만 특별히 스레드에서 동작할 수 있다는 것이 아니다.
Runnable
을 구현한 클래스는 스레드 생성 시 동작할 메서드를 명시적으로 정의하기 위해 사용된다.
왜 스레드를 실행시키는 두 가지 방법을 지원할까?
자바는 다중 상속이 불가능하기 때문이다.
A라는 클래스가 B를 상속받아 확장하는 관계에서 Thread를 상속받을 수 없으니 인터페이스를 통한 구현을 통해 해결하기 위함이다.
멀티스레드 환경에선 동기화를 신경써야 한다.
Deamon Thread
☁️ 질문
- 스레드란 무엇인가?
- 스레드가 왜 필요할까?
- 스레드와 프로세스의 차이는?
- 자바에서 스레드를 생성하고 실행하는 방법은?
- 특정 스레드의 작업이 종료되기를 기다리는 메서드는?
- synchronized란 무엇인가?
- synchronized가 왜 필요한가?
- synchronized를 통해 동기화 처리되는 프로세스를 설명해달라.
- threadGroup이란 무엇인가?
'JAVA > 자바의 신' 카테고리의 다른 글
27장. Serializable과 NIO도 살펴 봅시다. (1) | 2024.01.11 |
---|---|
26장. 파일에 있는 것을 읽고 쓰려면 아이오를 알아야죠 (0) | 2024.01.10 |
23, 24장. 자바랭 다음으로 많이 쓰는 애들은 컬렉션 - Set, Queue, Map (2) | 2024.01.10 |
22장. 자바랭 다음으로 많이 쓰는 애들은 컬렉션 - List (1) | 2024.01.10 |
21장. 실수를 방지하기 위한 제네릭이라는 것도 있어요 (0) | 2024.01.09 |