티스토리 뷰
목표
자바의 멀티쓰레드 프로그래밍에 대해 학습하기
학습할 것
- Thread 클래스와 Runnable 인터페이스
- 쓰레드의 상태
- 쓰레드의 우선순위
- Main 쓰레드
- 동기화
- 데드락
1. Thread 클래스와 Runnable 인터페이스
멀티 스레드로 실행하는 애플리케이션을 개발할 때는 몇 개의 작업을 병렬로 실행할지 결정하고 각 작업별로 스레드를 생성해야 한다.
어떤 자바 애플리케이션이건 메인 스레드는 반드시 존재하므로 메인 작업 이외 추가적인 병렬 작업의 수만큼 스레드를 생성하면 된다.
자바에서는 작업 스레드도 객체로 생성되기 때문에 클래스가 필요하며 2가지 방법으로 스레드를 생성할 수 있다.
-
java.lang.Thread 클래스를 직접 객체화해서 생성
-
Thread를 상속해 하위 클래스를 만들어 생성
java.lang.Thread 클래스로부터 작업 스레드 객체를 직접 생성하려면 Runnable을 매개값으로 가지는 생성자를 호출해야 한다.
Thread thread = new Thread(Runnable target);
Runnable은 작업 스레드가 실행할 수 있는 코드를 가지고 있는 객체이다.
Runnable은 인터페이스 타입이기 때문에 구현 객체를 만들어 대입해야 하며, Runnable에는 run() 메소드가 하나 정의되어 있는데 구현 클래스는 run()을 재정의해 작업 스레드가 실행할 코드를 작성해야 한다.
class Task implements Runnable {
public void run() {
// 스레드가 실행할 코드;
}
}
2. 쓰레드의 상태
스레드는 객체를 생성하고, start() 메소드를 호출하면 곧바로 스레드가 실행되는 것처럼 보이지만 실행 대기 상태가 된다.실행 대기 상태란 아직 스케줄링이 되지 않아 실행을 기다리고 있는 상태를 말한다.
실행 대기 상태에 있는 스레드 중 스레드 스케줄링으로 선택된 스레드가되면 CPU를 점유하고 run() 메소드를 실행한다.→ 실행(Running) 상태
실행 상태의 스레드는 run() 메소드를 모두 실행하기 전에 스레드 스케줄링에 의해 다시 실행 대기 상태로 돌아갈 수 있다.그리고 실행 대기 상태에 있는 다른 스레드가 선택되어 실행된다. 이렇게 스레드는 실행 대기 상태와 실행 상태를 번갈아가면서 자신의 run() 메소드를 조금씩 실행하게 된다.
실행 상태에서 run() 메소드가 종료되면 더이상 실행할 코드가 없기 때문에 스레드의 실행을 멈추게 된다. → 종료 상태
javaTpoint, Life cycle of a Thread (Thread States),
www.javatpoint.com/life-cycle-of-a-thread
3. 쓰레드의 우선순위
멀티 스레드는 동시성(Concurrency) 또는 병렬성(Parallelism)으로 실행된다.
-
동시성 : 멀티 작업을 위해 하나의 코어에서 멀티 스레드가 번갈아가며 실행하는 성질
-
병렬성 : 멀티 작업을 위해 멀티 코어에서 개별 스레드를 동시에 실행하는 성질
스레드의 개수가 코어의 수보다 많을 경우 스레드를 어떤 순서에 의해 동시성으로 실행할 것인지 결정해야 한다.
이것을 스레드 스케줄링이라 부르며, 스레드 스케줄링에 의해 스레드들은 짧은 시간에 번갈아가며 그들의 run() 메소드를 실행한다.
자바의 스레드 스케줄링은 우선순위(Priority) 방식과 순환 할당(Round-Robin) 방식을 사용한다.
- 우선순위 방식 : 우선순위가 높은 스레드가 실행 상태를 더 많이 가지도록 스케줄링하는 것
- 순환 할당 방식 : 시간 할당량을 정해 하나의 스레드를 정해진 시간 만큼 실행하고 다시 다른 스레드를 실행하는 것
우선순위 방식은 스레드 객체에 우선 순위 번호를 부여할 수 있기 때문에 개발자가 코드로 제어할 수 있다.반면 순환 할당 방식은 JVM에 의해 정해지기 때문에 코드로 제어할 수 없다.
우선순위 방식에서 우선순위는 1에서 10까지 부여되는데, 1이 우선순위가 가장 낮고, 10이 가장 높다.우선순위를 부여하지 않는 경우 모든 스레드들이 기본적으로 5의 우선순위를 할당받는다.
만약 우선순위를 변경하고 싶다면 Thread 클래스가 제공하는 setPriority() 메소드를 이용하면 된다.
thread.setPriority(우선순위);
thread.setPriority(Thread.MAX_PRIORITY);
thread.setPriority(Thread.NORM_PRIORITY);
thread.setPriority(Thread.MIN_PRIORITY);
우선순위 매개값으로 1~10까지의 값을 직접 주어도 되지만, 코드의 가독성을 높이기 위해 Thread 클래스의 상수를 사용할 수 있다.
4. Main 쓰레드
모든 자바 애플리케이션은 메인 스레드가 main() 메소드를 실행하면서 시작된다.
메인 스레드는 main() 메소드의 첫 코드부터 아래를 순차적으로 실행한다.
메인 스레드는 필요에 따라 작업 스레드들을 만들어 병렬로 코드를 실행할 수 있으며 이것을 멀티 태스킹을 수행한다고 말한다.
싱글 스레드 애플리케이션에서는 메인 스레드가 종료되면 프로세스도 종료되지만, 멀티 스레드 애플리케이션의 경우 실행 중인 스레드가 하나라도 있는 경우 프로세스는 종료되지 않는다.
메인 스레드가 작업 스레드보다 먼저 종료되더라도 작업 스레드가 계속 실행 중이라면 프로세스는 종료되지 않는다.
5. 동기화
싱글 스레드 프로그램에서는 한 개의 스레드가 객체를 독차지해서 사용하면 되지만, 멀티 스레드 프로그램에서는 스레드들이 객체를 공유해서 작업해야 하는 경우가 있다. 이 경우, 스레드 A를 사용하던 객체가 스레드 B에 의해 상태가 변경될 수 있기 때문에 스레드 A가 의도했던 것과 다른 결과를 산출할 수도 있다.
스레드가 사용 중인 객체를 다른 스레드가 변경하지 않도록 하려면 작업이 끝날 때 까지 다른 스레드가 사용할 수 없도록 해야 한다.
단 하나의 스레드만 실행할 수 있는 코드 영역을 임계 영역(critical section)이라 하며, 자바는 임계 영역을 지정하기 위해 동기화 메소드와 동기화 블록을 제공한다. 동기화를 위해서는 synchronized 키워드를 사용한다.
public synchronized void method() {
// 임계 영역
}
public void method() {
// 여러 스레드가 실행 가능한 영역
synchronized(공유객체) {
// 임계 영역, 단 하나의 스레드만 실행 가능한 영역
}
// 여러 스레드가 실행 가능한 영역
}
6. 데드락
데드락은 교착 상태를 의미하며 다수의 쓰레드가 같은 lock 을 동시에 획득하려 할 때 발생할 수 있다.
예를 들면 1번 쓰레드가 A에 대한 락을 가지고 있는 상태에서 B에 대한 락을 획득하려고 하는데 2번 쓰레드가 B에 대한 락을 가지고 A에 대한 락을 획득하려고 한다면 이 경우 데드락이 발생할 수 있다. 2개의 쓰레드 모두 각자 A와 B에 대한 락을 가지고 있는 상태에서 영원히 블록 상태에 빠지게 되는 것이다.
GeeksforGeeks, Deadlock in Java Multithreading,
www.geeksforgeeks.org/deadlock-in-java-multithreading/
'Project > 자바 라이브 스터디' 카테고리의 다른 글
자바 라이브 스터디 #12 : 애노테이션 (0) | 2021.03.05 |
---|---|
자바 라이브 스터디 #11 : Enum (0) | 2021.03.05 |
자바 라이브 스터디 #9 예외 처리 (0) | 2021.01.15 |
자바 라이브 스터디 #7 패키지 (0) | 2020.12.31 |
자바 라이브 스터디 #6 상속 (0) | 2020.12.23 |