[Java] 쓰레드 (Thread) 2
함수를 쓰게 되면 운영체제가 자신의 시스템에 맞게 조절을 하게 되어, 프로그래머가 컨트롤 할 수 있는 부분이 그렇게 크지는 않다. 하지만, 이런 제한된 컨트롤 가능 범위 내에서 쓰레드의 실행을 어떻게 제어할 것인지를 알아보자.
쓰레드의 실행을 제어하기 위해 사용하는 메서드들을 살펴보자.
이 메서드들 중에서 stop suspend resume 메서드는 호환성을 위해서 정의돼있긴 하지만, 쓰레드의 비정상적인 종료에 큰 영향을 미치는 메서드들이기 때문에 자바에서 사용을 권장하지 않는다.
이 메서드들을 하나하나 살펴보기 전에 쓰레드의 상태에 대해 알고있어야 한다.
쓰레드에는 여러 가지 상태가 있는데, 쓰레드의 여러 가지 메서드들을 통해 어떤 상태를 다른 상태로 바꿔 줄 수도 있다.
일단 start() 메서드를 통해 실행하게 되면 쓰레드가 RUNNABLE 이라는 대기 큐에 들어가고, 들어간 순서대로 실행되고 실행이 끝나면 종료된다.
위의 과정은 정상적으로 쓰레드가 실행되는 과정인데, 만약 실행되는 과정에서 실행대기 과정으로 옮기고 싶으면 yield메서드를 사용할 수 있고, 실행 도중 강제로 종료하고 싶으면 stop 메서드를 사용할 수 있다. (권장되지 않는 함수지만)
위의 상태를 보면 WATITING 과 BLOCKED 상태도 있다. WAITING 상태는 쓰레드가 실행되다가 일시정지된 상태라고 이해하고, BLOCKED 상태는 일시정지 상태지만 lock이 걸린 상태라고 이해하자. (lock은 한 가지의 쓰레드만 실행될 수 있는 공간이라고 생각하자.)
실행 중인 쓰레드를 일시 정지 상태로 바꾸려면 suspend sleep wait join 등의 메서드를 사용할 수 있고,
일시정지된 쓰레드를 다시 실행대기 상태로 바꾸려면 resume notify interrupt 등의 메서드를 사용할 수 있다.
이제 메서드들을 하나하나 살펴보자.
sleep
쓰레드를 재운다. WAITING 상태로 쓰레드를 옮긴다. 1000 은 1초. (milisecond 단위) 천분의일초.
매개변수를 두 가지 받는 경우 (milisecond, nanosecond(10^-9)) (더 세부적으로 지정할수 있다.)
sleep 메서드 사용 시 항상 예외처리를 해 줘야 한다. InterruptedException이 발생하면 깨어나게 된다.
try {
Thread.sleep(1,5000000);
}catch(InterruptedException e ) {
}
sleep 메서드를 사용하면 현재 실행중인 쓰레드가 자게 되고, 특정 쓰레드를 지정해서 멈추게 할 수는 없다.
static 메서드이기 때문에 클래스명.sleep() 로 사용한다. ******중요********
interrupt
대기상태인 쓰레드를 실행대기상태로 바꾼다.
interrupt는 방해하다 라는 뜻인데.. 직관과는 다르게 실행대기상태로 만드는 메서드이다.
interrupt 메서드를 활용해 조건문으로 활용할 수 있다.
코드를 이해하는게 어렵지 않을 것이다.
for 문으로 시간끄는것보다 sleep문으로 시간을 끌어보면
위의 코드처럼 바꿀 수 있다.
suspend, resume, stop
suspend 메서드는 쓰레드를 지정해 줄 수 있다. sleep과 비슷하지만, resume 메서드를 호출하기 전까지 WAITING 상태에 있는다.
위 세 가지 메서드들은 매우 위험한 메서드이고, 사용하고 싶으면 직접 구현해야 한다.
사용 예시를 알아보자.
volatile 제어자는 메인 메모리에서 읽어오라는 의미를 가진다.
자세한건 컴퓨터구조 과목에서 배우지만, 보통 컴퓨터는 값들을 메인 메모리까지 가지 않고 CPU에서 바로 읽는다. 하지만 쓰레드를 사용할 경우 (특히 멀티코어 이상을 사용할 경우) 실제 값이 캐시에 반영되지 않을 가능성이 있기 때문에 메인 메모리에서 직접 읽어오도록 하기 위해 volatile 제어자를 사용한다. (성능은 떨어질 수 있다.)
yield
현재 실행 중인 쓰레드를 실행대기상태로 옮긴다.
Runnable은 함수형 인터페이스이기때문에 람다식으로 구현할 수 있다.
쓰레드를 스케줄링하는 운영체제에 따라 결과값이 다르게 나타난다.
join
지정된 시간동안 특정 쓰레드가 작업하는 걸 기다린다.
sleep과 비슷하지만, 어떤 쓰레드를 대기상태로 설정할 지 지정해 줄 수 있다.
사용 시 예외처리를 해 줘야 한다. (InterruptedException이 발생하면 작업 재개)
메인 쓰레드가 쓰레드1과 쓰레드2가 끝날 때 까지 기다린 다음 메인 쓰레드가 실행하는데에 걸린 시간을 출력한다.
쓰레드는 전역변수를 공유하지만, 쓰레드의 실행 순서는 정해지지 않았다. 이런 이유로 쓰레드들끼리 정보를 공유하는 과정에서 충돌이 발생할 수 있다. 이런 충돌을 발생하는걸 방지하기 위해 잠금을 설정하는 등 여러 해결책이 있는데, 그 중 동기화에 대해 알아보자. 사실 이런 부분은 운영체제 과목에서 자세하게 다루기 때문에 자세한 내용은 다음에 알아보기로 하고 지금은 어떻게 활용하는지에 집중해 공부하자.
쓰레드의 동기화 (Synchronized)
한 번에 하나의 쓰레드만 객체에 접근할 수 있도록 객체에 lock을 설정해 객체를 보호한다.
synchronized 제어자를 통해 lock을 걸어줄 수 있다.
코드를 통해 예시를 확인하자.
synchronized가 없으면 변수가 음수로 지정될 수 있다.
'Programming Language > Java' 카테고리의 다른 글
[Java] 입출력 (I/O) 2 (0) | 2021.11.29 |
---|---|
[Java] 입출력 (I/O) 1 (0) | 2021.11.21 |
[Java] 쓰레드 (Thread) 1 (0) | 2021.11.08 |
[Java] 래퍼 클래스 (Wrapper) (0) | 2021.10.29 |
[Java] 예외처리 (0) | 2021.10.29 |
댓글
이 글 공유하기
다른 글
-
[Java] 입출력 (I/O) 2
[Java] 입출력 (I/O) 2
2021.11.29 -
[Java] 입출력 (I/O) 1
[Java] 입출력 (I/O) 1
2021.11.21 -
[Java] 쓰레드 (Thread) 1
[Java] 쓰레드 (Thread) 1
2021.11.08 -
[Java] 래퍼 클래스 (Wrapper)
[Java] 래퍼 클래스 (Wrapper)
2021.10.29