-
Java - ThreadJava/java study 2022. 2. 20. 22:32반응형
Thread
개념 정리
프로세스(Process)
- 프로그램을 실행하면 OS로부터 실행에 필요한 자원(메모리)을 할당받아 프로세스가 된다.
- 실행 중인 프로그램을 의미
- 프로세스가 작업을 수행하는 것이 아닌 스레드가 프로세스로부터 자원을 할당받아 수행한다.
- 구성 : 프로그램에 사용되는 데이터, 메모리 등의 자원과 스레드
스레드(Thread)
- 프로세스로부터 자원을 할당받아 실제로 작업을 처리
- 경량 프로세스라고 불리며 가장 작은 실행 단위이다.
- 한번 사용한 스레드는 재사용이 불가하다.
- 모든 프로세스에는 최소 하나 이상의 스레드가 존재
- 싱글 스레드 : 스레드가 하나인 경우
- 멀티 스레드 : 둘 이상의 스레드
멀티 태스킹(multi-tasking)
- 하나의 프로세서가 여러 개의 업무(프로세스)를 동시에 처리
- 하나의 프로세스의 처리과정을 기다리는 동안 다른 프로세스를 실행
- 즉, 동시에 여러 개의 프로그램을 실행한다는 것을 의미
- ex) 게임을 하면서 카카오톡도 동시에 사용 - 스케줄링 방식
1. 멀티프로그래밍 방식(Multi-programming)
2. 시분할 방식(Time-sharing)
3. 실시간 시스템 방식(Real-time)
멀티 쓰레딩(multi-threading)
- 하나의 프로세스가 동시에 여러 개의 스레드를 수행(병렬 처리)
- 프로세스를 생성하는 비용보다 스레드를 생성하는 비용이 더 저렴
- 스레드는 스택 영역을 제외한 데이터, 힙 영역을 서로 공유
Thread 생성 방법과 실행
Thread 클래스
- 스레드를 생성할 때 사용하는 클래스
- run() 메서드를 오버라이딩하여 수행할 작업을 작성
예시)
Runnable 인터페이스
- 스레드를 생성할 때 사용되는 인터페이스
- run() 메서드를 오버라이딩하여 수행할 작업을 작성
- 재사용성이 높고 코드의 일관성을 유지할 수 있다.(객체지향적인 방법)
- Thread 클래스를 상속받으면 다른 클래스를 상속받을 수 없기 때문에
Runnable 인터페이스를 구현하는 방법이 일반적이다.
예시)
멀티 스레드 예시
스레드를 생성하지 않으면 프로세스는 스레드가 싱글 스레드로 실행이 된다. 다음 메서드 2개를 호출했을 때 예시를 보자.
위와 같이 메서드를 호출하는 순서대로 처리가 된다. 즉, printTest0 메서드가 호출되어 for문이 다 실행되고, 메서드에서 모든 처리가 끝나면 printTest1 메서드를 호출하여 처리한다. 그럼 멀티 스레드 방식으로 하면 어떤 결과가 나오는지 보자.
위와 같이 멀티 스레드 방식으로 실행하게 되면 스레드가 번갈아가면서 처리하는 모습을 볼 수 있다. 위 예시는 Main 스레드 1개와 생성한 스레드 2개가 번갈아 가면서 실행되고 있다. 이처럼 스레드가 번갈아가면서 수행되기 때문에 우리는 마치 동시에 실행되는 것처럼 보인다.
Thread 상태와 메서드
자원을 효율적으로 사용하기 위해서는 멀티 스레드를 잘 사용해야 하는데 이를 위해 스레드의 상태와 관련 메서드를 알아보자.
스레드 상태
상태 설명 NEW 쓰레드가 생성되고 아직 start()가 호출되지 않은 상태 RUNNABLE 실행 중 또는 실행 가능한 상태 BLOCKED 동기화블럭에 의해서 일시정지된 상태(lock이 풀릴 때까지 기다리는 상태) WAITING, TIMED_WAITING 쓰레드의 작업이 종료되지는 않았지만 실행 가능하지 않은 일시정지 상태,
TIMED_WATING은 일시정지 시간이 지정된 경우를 말함TERMINATED 쓰레드의 작업이 종료된 상태
스레드 상태 제어 메서드
메서드 설명 static void sleep(long millis)
static void sleep(long millis, int nanos)지정된 시간동안 쓰레드를 일시정지시키다.
지정한 시간이 지나고 나면, 자동적으로 다시 실행대기 상태가 된다.void join()
void join(long millis)
void join(long millis, int nanos)지정된 시간동안 쓰레드가 실행되도록 한다. join()을 호출한 쓰레드는 그동안 일시정지 상태가 되며 지정된 시간이 지나거나 작업이 종료되면 join()을 호출한 쓰레드로 다시 돌아와 실행을 계속한다. void interrupt() sleep()이나 join()에 의해 일시정지 상태인 쓰레드를 깨워서 실행대기 상태로 만든다. 해당 쓰레드에서는 InterruptedException이 발생함으로써 일시정지 상태를 벗어나게 된다. void stop()쓰레드를 즉시 종료시킨다. void suspend()쓰레드를 일시정지시킨다. resume()을 호출하면 다시 실행대기 상태가 된다. void resume()suspend()에 의해 일시정지 상태에 있는 쓰레드를 실행대기 상태로 만든다. static void yield() 실행 중에 자신에게 주어진 실행시간을 다른 쓰레드에게 양보하고 자신은 실행대기 상태가 된다. ※ resume(), stop(), suspend()는 스레드를 교착상태(dead-lock)로 만들기 쉽기 때문에 사용되지 않는다.
스레드 상태 확인 메서드
메서드 설명 void checkAccess() 현재 수행중인 쓰레드가 해당 쓰레드를 수정할 수 있는 권한이 있는지를 확인한다. 만약 권한이 없다면 SecurityException 예외 발생 boolean isAlive() 쓰레드가 살아 있는지를 확인한다. 해당 쓰레드의 run() 메서드가 종료되었는지를 확인 boolean isInterrupted() run() 메서드가 정상적으로 종료되지 않고, interrupt() 메서드의 호출을 통해서
종료되었는지 확인static boolean interrupted() 현재 쓰레드가 중지되었는지를 확인 static int activeCount() 현재 쓰레드가 속한 쓰레드 그룹의 쓰레드 중 살아 있는 쓰레드의 개수를 리턴 static Thread currentThread() 현재 수행중인 쓰레드의 객체를 리턴 static void dumpStack() 콘솔 창에 현재 쓰레드의 스택 정보를 출력 이 외에도 Object 클래스에도 wait(), norify(), notify() 메서드들이 있다.
Thread 우선순위
스레드는 우선순위라는 멤버 변수를 갖고 있다. 이 우선순위의 값에 따라 스레드가 얻는 수행 시간이 달라진다. 따라서 중요도가 높은 스레드는 우선순위를 높게 지정하여 작업 수행 시간을 늘리고, 중요도가 낮은 스레드는 우선순위를 낮게 지정하여 작업 수행 시간을 줄일 수 있다.
- getPriority() : 스레드의 현재 우선순위 반환
- setPriority(int newPriority) : 스레드의 우선순위 변경
필드 설명 public static final int MIN_PRIORITY = 1; 쓰레드가 가질 수 있는 최소 우선순위 public static final int NORM_PRIORITY = 5; 쓰레드가 생성될 때 가지는 기본 우선순위 public static final int MAX_PRIORITY = 10; 쓰레드가 가질 수 있는 최대 우선순위
Main 스레드
'Java의 실행환경인 JVM은 하나의 프로세스로 실행됩니다'
자바 애플리케이션이 기본적으로 하나의 메인 스레드를 갖는다는 뜻이다. 우리가 애플리케이션 클래스를 생성하면 만들어주는 메서드 psvm(public static void main(String[] args))가 바로 메인 스레드이다.
프로그램을 실행하면 기본적으로 하나의 스레드를 생성하고, 그 스레드가 main 메서드를 호출해서 작업이 수행이 되며 따로 스레드를 실행하지 않고 main 메서드만 실행하는 것을 싱글 스레드 애플리케이션이라고 하고, 메인 스레드에서 스레드를 생성하여 실행하는 것을 바로 멀티 스레드 애플리케이션이라 한다.
Daemon Thread는 메인 스레드의 작업을 돕는 보조적인 역할을 하는 스레드인데 메인 스레드가 종료되면 Daemon Thread도 강제적으로 자동 종료가 된다. 이 점을 제외하고는 일반 스레드와 차이가 없으며 예로 가비지 컬렉터가 있다.
동기화(Synchronization)
멀티 스레드 프로세스에서는 여러 프로세스가 메모리를 공유하기 때문에 한 스레드가 사용하고 있던 공유 필드를 다른 스레드가 간섭하는 문제가 생길 수 있다. 다음 예시를 보자.
위와 같은 일이 만약 서비스 업체, 은행 같은 곳에서 발생하게 된다면 엄청난 이슈가 된다. 따라서 공유 자원을 사용할 때는 현재 사용하는 스레드를 제외한 나머지 스레드들이 접근하지 못하게 막아야 하는데 이것을 바로 동기화(Synchronize)라 한다.
synchronized
동기화를 적용하기 위해서는 다른 스레드가 간섭해서는 안 되는 부분을 sysnchronized 키워드를 이용해 임계 영역(critical section)으로 설정해주어야 한다.
// 메서드 전체를 임계영역으로 설정 public synchronized void method1(){ } // 특정한 영역을 임계영역으로 설정 synchronized(객체의 참조변수){ } // 예시 public synchronized void withdraw(int money) { if (balance >= money) { balance -= money; } } public void withdraw(int money) { synchronized (this) { if (balance >= money) { balance -= money; } } }
교착상태(DeadLock)
교착상태란 둘 이상의 스레드들이 서로의 lock을 획득하기 위해 대기하는 상태를 말한다.
교착상태의 조건
- 상호 배제 : 프로세스들이 필요로 하는 자원에 대해 배타적인 통제권을 요구
- 점유 대기 : 프로세스가 할당된 자원을 가진 상태에서 다른 자원을 대기
- 비선점 : 프로세스가 어떤 자원의 사용을 끝낼 때까지 그 자원을 독점
- 순환 대기 : 각 프로세스는 순환적으로 다음 프로세스가 요구하는 자원을 갖는다.
교착상태 해결 방법
- 교착상태 예방 : 교착상태를 유발하는 네 가지 조건을 무력화
- 교착상태 회피 : 교착상태가 발생하지 않는 수준으로 자원을 할당
- 교착상태 검출 : 자원 할당 그래프를 사용하여 교착상태를 검출
- 교착상태 회복 : 교착상태를 검출한 후 해결
참고
스레드의 동기화 방법은 synchronized 이외에 lock 클래스를 사용할 수 있다.반응형'Java > java study' 카테고리의 다른 글
Java - 애노테이션(annotation) (0) 2022.03.06 Java - enum이란 (0) 2022.02.27 Java - 예외 처리 (0) 2022.02.13 Java - 인터페이스(interface) (0) 2022.02.06 Java - 클래스패스(ClassPath) (0) 2022.01.23