스레드 구현
자바에서 스레드 구현 방법은 2가지가 있다.
- Runnable 인터페이스 구현
- Runnable 인터페이스를 구현한 경우는, 해당 클래스를 인스턴스화해서 Thread 생성자에 argument로 넘겨줘야 한다.
- Thread 클래스 상속
- 둘다 run() 메소드를 오버라이딩 하는 방식이다.
- 자바는 단일 상속 만을 지원하기 때문에 Runnable 인터페이스를 구현하는 것이 많은 이점을 가져갈 수 있다.
스레드 실행
스레드의 실행은 run()이 아닌 start()호출로 해야한다.
- 분명 run() 메서드를 재정의 했지만 실제 스레드 작업을 시키려면 start()로 작업해야한다.
- run()으로 작업지시를 하면 스레드를 사용하는 것이 아니게 된다.
- Java에는 콜 스택이 있다. 이 영역이 실질적인 명령어를 담고 있는 메모리로 , 하나씩 꺼내서 실행시키는 역할을 한다.
- 만약 동시에 두 가지 작업을 하기 위해선, 두 개 이상의 콜스택이 필요함
- 스레드를 이용한다는 것은, JVM이 다수의 콜 스택을 번갈아가면 처리를 하는 것이고 사용자에겐 동시에 작업하는 것 처럼 보이게 된다.
- 즉 run() 메서드를 이용하는 것은 main()의 콜스택에 이용하게 되는 것이고
- start() 메서드를 호출하면 JVM은 알아서 스레드를 위한 콜 스택을 새로 만들어 주고 새로운 콜스택에 run()을 호출하게 된다.
스레드의 I/O블락킹
- 입출력시 작업 중단되는 현상
- 싱글스레드의 경우 I/O을 기다리면서 아무일도 하지 않는다.
- 멀티스레드의 경우 I/O을 기다리는 것과 동시에 다른 일을 할 수 있다.
스레드의 우선순위
작업의 중요도에 따라 스레드의 우선순위를 다르게 하여 특정 스레드가 더 많은 작업시간을 갖게 할 수 있다.
- 자바에서는 1~10까지 부여할 수 있고 default를 5이다.
- setPriority(int newPriority)함수를 통해 변경할 수 있다.
- 실제로는 참고만 할 뿐 무조건 먼저 실행되지는 않는다.
- OS 스케줄러가 담당
- 확률을 높이는 것을 기대함
데몬 스레드
일반 스레드의 작업을 돕는 보조적인 역할을 수행
- 일반 스레드
- 데몬 스레드
- 일반 스레드의 작업을 돕는 보조적인 역할을 수행
- 일반 스레드가 모두 종료되면 자동으로 종료
- GC, 자동저장, 화면 자동갱신 등에 사용된다.
- 무한루프와 조건문을 이용해서 실행 후 대기하다가 특정 조건이 만족되면 작업을 수행하고 다시 대기하도록 작성한다.
void setDaemon(boolean on) //스레드를 데몬 스레드로 또는 사용자 스레드로 변경 //매개변수 on을 true -> 데몬스레드가 된다.
- setDaemon(boolean on)은 반드시 start()를 호출하기 전에 실행되어야 한다. 그렇지 않으면 IllegalTreadStateException이 발생.
스레드 그룹
서로 관련된 스레드를 그룹으로 묶어서 다루기 위한 것
- 모든 스레드는 반드시 하나의 스레드 그룹에 포함되어 있어야 한다.
- 지정하지 않고 생성하면 main스레드 그룹에 속한다.
- 자신을 생성한 스레드의 그룹과 우선순위를 상속받는다.
스레드의 상태
스레드의 상태는 5가지가 있다.
- NEW
- 스레드가 생성되고 아직 start()가 호출되지 않은 상태
- RUNNABLE
- 실행 중 또는 실행 가능 상태
- BLOCKED
- 동기화 블럭에 의해 일시정지된 상태
- WAITING, TIME_WAITING
- 실행가능하지 않은 일시정지 상태
- TERMINATED
- 스레드 작업이 종료된 상태
스레드의 실행제어
스레드의 실행을 제어할 수 있는 메서드가 제공된다.
- static sleep(long m) static sleep(long m, int n)
- 지정된 시간동안 스레드를 일시정지시킴. 지정된 시간이 지나면 자동으로 실행대기상태가 됨.
- 특정 스레드를 지정해서 멈추게 하는 것은 불가능하다.
- void join() void join(long m) void join(long m, int n)
- 지정된 시간동안 스레드가 실행되도록 한다. 지정된 시간이 지나거나 작업이 종료되면 join()을 호출한 스레드로 다시 돌아와 실행을 계속함.
- void interrupt()
- sleep()이나 join()에 의해 일시정지상태인 스레드를 깨워 실행대기 상태로 만든다. 해당 스레드에서는 interrupted exception이 발생함으로써 일시정지 상태를 벗어나게 된다.
- void stop()
- 스레드를 즉시 종료시킨다.
- void suspend()
- 스레드를 일시정지시킨다. resume()을 호출하면 다시 실행대기상태가 된다.
- void resume()
- suspend()에 의해 일시정지상태에 있는 스레드를 실행대기상태로 만든다.
- static void yield()
- 실행중에 자신에게 주어진 실행시간을 다른 스레드에게 양보하고 자신은 실행대기상태가 된다.
- yield, interrupt 메서드를 적절히 사용하면, 응답성과 효율을 높일 수 있다.
- suspend(), resume(), stop()
- 이 메서드들은 dead-lock을 발생시키기 쉽기 때문에 사용을 지양하길 권장하고 있다.
스레드의 동기화(synchronization)
멀티 스레드 프로세스에서는 다른 스레드의 작업에 영향을 미칠 수 있다.
- 진행중인 작업이 다른 스레드에게 간섭받지 않게 하려면 ‘동기화’가 필요
- 스레드의 동기화 - 한 스레드가 진행중인 작업을 다른 스레드가 간섭하지 못하게 막는 것.
- 동기화 하려면 간섭받지 않아야 하는 문장들을 ‘임계 영역’으로 설정
- 임계영역은 lock을 얻은 단 하나의 스레드만 출입가능(객체 1개에 lock 1개)
synchronized를 이용한 동기화
- synchronized로 임계영역(lock이 걸리는 영역)을 설정하는 방법 2가지
- 메서드 전체를 임계영역으로 지정
public synchronized void calSum(){ //... }
- 특정한 영역을 임계 영역으로 지정
synchronized(객체의 참조변수){ //... }
- 임계 영역은 많으면 많을수록 성능이 떨어지기 때문에 최소화 하는 것이 좋다.
wait()와 notify()
동기화를 하면 효율이 떨어진다. 동기화의 효율을 높이기 위해 wait(), notify()를 사용.
- Object클래스에 정의되어 있으며, 동기화 블록 내에서만 사용할 수 있다.
- wait()
- 객체의 lock을 풀고 스레드를 해당 객체의 waiting pool에 넣는다.
- notify()
- waiting pool에서 대기중인 쓰레드 중의 하나를 깨운다.
- notifyAll()
- waiting pool에서 대기중인 모든 스레드를 깨운다.
- wait()
'Computer science > JAVA' 카테고리의 다른 글
JVM (0) | 2023.03.16 |
---|---|
Instrinsic Lock (0) | 2023.01.08 |
Casting (0) | 2023.01.01 |
Object Class (0) | 2023.01.01 |
String Class (0) | 2023.01.01 |