프로세스와 스레드
•
프로세스 : 실행 중인 프로그램, 자원(resource)과 스레드로 구성
•
스레드 : 프로세스 내에서 실제 작업을 수행. 모든 프로세스는 최소한 하나의 스레드를 갖고 있다
•
하나의 새로운 프로세스를 생성하는 것보다 하나의 새로운 스레드를 생성하는 것이 더 적은 비용이 든다
멀티스레드의 장단점
•
장점
◦
시스템 자원을 보다 효율적으로 사용할 수 있다
◦
사용자에 대한 응답성이 향상된다
◦
작업이 분리되어 코드가 간결해진다
•
단점
◦
동기화에 주의해야 한다
◦
교착상태가 발생하지 않도록 주의해야 한다
◦
각 스레드가 효율적으로 고르게 실행될 수 있게 해야 한다
스레드의 구현과 실행
•
Thread 클래스를 상속 (Bad! 단일 상속의 한계)
•
Runnable 인터페이스를 구현 (Better! 다른 클래스 상속 가능)
스레드의 실행 - start()
•
스레드를 생성한 후에 start()를 호출해야 스레드가 작업을 시작한다
•
스레드를 시작하면 시작 가능 상태가 되고 OS 스케줄러가 실행 순서를 결정한다
start()와 run()
•
왜 run()을 만들었는데 start()를 호출하는거지???
→ start()를 통해 새로운 스택을 만들어 run()을 호출하고 start()는 종료된다
main 스레드
•
main 메서드의 코드를 수행하는 스레드
•
스레드는 ‘사용자 스레드'와 ‘데몬 스레드' 두 종류가 있다
•
실행 중인 사용자 스레드가 하나도 없을 때 프로그램은 종료된다
싱글스레드 vs 멀티스레드
•
시간은 똑같지 않고 OS 스케줄러에 의해 달라진다
스레드의 I/O blocking
스레드의 우선순위
•
작업의 중요도에 따라 스레드의 우선순위를 다르게 하여 특정 스레드가 더 많은 작업시간을 갖게 할 수 있다
•
JVM에서의 우선순위는 1~10까지 보통 우선순위는 5이다
•
Windows OS에서의 우선순위는 32단계로 나눠져 있다
스레드 그룹
•
서로 관련된 스레드를 그룹으로 묶어서 다루기 위한 것
•
모든 스레드는 반드시 하나의 스레드 그룹에 포함되어 있어야 한다
•
스레드 그룹을 지정하지 않고 생성한 스레드는 main 스레드 그룹에 속한다
•
자신을 생성한 스레드(부모 스레드)의 그룹과 우선순위를 상속 받는다
스레드 그룹의 메서드
데몬 스레드
•
일반 스레드의 작업을 돕는 보조적인 역할을 수행
•
일반 스레드가 모두 종료되면 자동적으로 종료된다
•
가비지 컬렉터, 자동저장, 화면 자동갱신 등에 사용된다
•
무한루프와 조건문을 이용해서 실행 후 대기하다가 특정조건이 만족되면 작업을 수행하고 다시 대기하도록 작성한다
스레드의 상태
스레드의 실행제어
•
스레드의 실행을 제어할 수 있는 메서드가 제공된다
sleep()
•
현재 스레드를 지정된 시간동안 멈추게 한다
•
예외처리를 해야 한다 (InterruptedException이 발생하면 깨어남)
•
특정 스데르를 지정해서 멈추게 하는 것은 불가능하다
interrupt()
•
대기상태(WAITING)인 스레드를 실행대기 상태(RUNNABLE)로 만든다
suspend(), resume(), stop()
•
스레드의 실행을 일시정지(suspend), 재개(resume), 완전정지(stop) 시킨다
•
교착상태(dead-lock)에 빠지기 쉬워서 deprecated 되었다
join()
•
지정된 시간동안 특정 스레드가 작업하는 것을 기다린다
•
예외처리를 해야한다 (InterruptedException이 발생하면 작업 재개)
yield()
•
남은 시간을 다음 스레드에게 양보하고 자신(현재 스레드)은 실행대기한다
•
yield()와 interrupt()를 적절히 사용하면 응답성과 효율을 높일 수 있다
스레드의 동기화(synchronization)
•
멀티 스레드 프로세스에서는 다른 스레드의 작업에 영향을 미칠 수 있다
•
진행중인 작업이 다른 스레드에게 간섭받지 않게 하려면 ‘동기화'가 필요
•
동기화하려면 간섭받지 않아야 하는 문장들을 ‘임계 영역'으로 설정
•
임계영역은 락(lock)을 얻은 단 하나의 스레드만 출입가능 (객체 1개에 락 1개)
synchronized를 이용한 동기화
•
synchronized로 임계영역(lock이 걸리는 영역)을 설정하는 방법 2가지
wait()과 notify()
•
동기화의 단점은 한 번에 한 스레드만 임계 영역에 들어가서 프로그램의 효율이 떨어진다
•
그 단점을 보완하기 위해 (=동기화의 효율을 높이기 위해) wait(), notify() 사용
•
Object 클래스에 정의되어 있으며 동기화 블록 내에서만 사용할 수 있다
◦
wait() : 객체의 lock을 풀고 스레드를 해당 객체의 waiting pool에 넣는다
◦
notify() : waiting pool에서 대기 중인 스레드 중의 하나를 깨운다
◦
notifyAll() : waiting pool에서 대기 중인 모든 스레드를 깨운다