자바의 정석 - 자바 기본기 정리하기 (12)쓰레드
들어가며
문제 구현에 있어서 자바에 대한 기본기의 부족함을 느껴서 오랜만에 자바의 기본 저서인 자바의 정석을 다시 피게 됐다. 그러면서 정말 신기한 경험을 하게 되었는데 바로 예전에 잘 이해가 안가서 읽고 넘어갔던 내용들이 이제는 내 머릿속에서 자연스럽게 그려지는 경험을 하게 되었다. 그동안에 시간들이 헛되지는 않았나보다.
어느 곳에서나 기본기는 중요하듯이 이번 기회를 통해 자바 기본기를 더 단단히 다지고자 챕터별로 글로 정리하면서 다시 한번 암기를 하고 좀 더 디테일하게 알아야 되는 곳은 챕터를 나눠서 자바의정석에 나온 내용 + 보강된 내용을 더해서 정리를 하고자 한다.
정리
프로세스와 쓰레드
프로세스란 간단히 말해 실행 중인 프로그램이다. 프로그램을 실행하면 os로 부터 실행에 필요한 메모리를 할당 받아 프로세스가 된다. 그리고 프로세스의 자원을 이용해서 실제로 작업을 수행하는 것이 바로 쓰레드이다. 그래서 모든 프로세스에는 최소한 하나 이상의 쓰레드가 존재하며 둘 이상의 쓰레드를 가진 프로세스를 멀티쓰레드 프로세스라고 한다.
멀티쓰레드의 장점
cpu의 사용률을 향상 시킴
자원을 보다 효울적으로 사용가능
사용자에 대한 응답성 향상
작업이 분리되어 코드 간결해짐
메신저로 채팅하면서 파일을 다운받는 것이 바로 멀티쓰레드여서 가능. 단일 쓰레드면 파일 다운 받는 동안 채팅 못함
그렇다고 장점만 있는 것은 아님. 동시 작업이 일어나기에 그러면서 발생할 수 있는 동기화, 교착상태와 같은 문제들을 고려해서 신중히 프로그래밍해야한다.
쓰레드의 구현과 실행
구현방법 두가지 -> Thread클래스 상속받거나 Runnable인터페이스 구현.
어느쪽이든 별 차이는 없지만 Thread클래스를 상속받으면 다른 클래스를 상속 못받으므로 Runnable인터페이스를 구현하는 방법이 일반적.
-> Runnable인터페이스의 run()을 구현해주면 됨
쓰레드의 실행
쓰레드를 생성했다고 해서 자동으로 실행되는 것은 아니다. start()를 호출해야만 쓰레드가 실행된다.
main메서드에서 run()을 호출하는 것은 생성된 쓰레드를 실행시키는 것이 아니라 단순히 클래스에 선언된 메서드를 호출하는 것일 뿐이다.
반면 start()는 새로운 쓰레드가 작업을 실행하는데 필요한 호출스택을 생성한 다음에 run()을 호출해서 생성된 호출스택에 run()이 첫번째로 올라가게 한다. 모든 쓰레드는 독립적인 작업을 수행하기 위해 자신만의 호출스택을 필요로 하기 때문에 새로운 쓰레드를 생성하고 실행시킬 때마다 새로운 호출스택이 생성되고 쓰레드가 종료되면 작업에 사용된 호출스택은 소멸된다.
메인쓰레드
main메서드의 작업을 수행하는 것도 쓰레드이며 이를 main쓰레드라고 한다. 만약 쓰레드가 하나 더 있다면 메인쓰레드가 종료되었더라도 프로그램이 종료되는 것이 아닌 다른 쓰레드까지 작업을 마쳐야 종료된다. 실행 중인 사용자 쓰레드가 하나도 없을때 프로그램은 종료되는 것.
쓰레드의 우선순위
1~10 이며 숫자가 높을수록 우선순위가 높다.
데몬쓰레드
데몬쓰레드는 다른 일반 쓰레드의 작업을 돕는 보조적인 역할을 수행하는 쓰레드이다. 일반 쓰레드가 모두 종료되면 데몬쓰레드는 강제적으로 자동 종료되는데 그 이유는 데몬쓰레드는 일반 쓰레드의 보조역할을 수행하므로 일반 쓰레드가 모두 종료된다면 의미가 없기 때문에 종료된다.
데몬 쓰레드의 예로는 가비지 컬렉터, 워드프로세서의 자동저장, 화면자동갱신등이 있다.
데몬쓰레드는 무한루프와 조건문을 이용해서 실행 후 대기하고 있다가 특정 조전이 만족되면 작업을 수행하고 다시 대기하도록 작성되어 있다.
쓰레드의 생성부터 소멸까지의 과정
1. 쓰레드를 생성하고 start()한다고 바로 실행되는 것이 아닌 실행대기열에 저장되어 자신의 차례가 될 때까지 기다림. (실행대기열은 큐와 같은 구조 FIFO)
2.실행대기 상태에 있다가 자신의 차레가 되면 실행 상태가 된다.
3. 주어진 실행시간이 다되거나 yield()를 만나면 다시 실행 대기상태가 되고 다음 차례의 쓰레드가 실행됨
4. 실행 도중에 suspend(), sleep(), wait(), join(), I/O block에 의해 일시정지상태가 될수 있다. l/O block은 입출력작업에서 발생하는 지연상태를 말한다. 사용자의 입력을 기다리는 경우이다. 사용자가 입력을 마치면 다시 실행 대기 상태가 된다.
5. 지정된 일시정지시간이 다되거나(time-out), notify(), resume(), interrupt()가 호출되면 일지정지상태를 벗어나 다시 실행 대기열에 저장되어 자신의 차례를 기다린다.
6. 실행을 모두 마치거나 stop()이 호출되면 쓰레드는 소멸된다.
쓰레드의 동기화
싱글쓰레드는 상관없지만 멀티쓰레드 이상에서는 동기화 필요. 이유는 쓰레드1이 작업도중 제어권이 2로 넘어가 2가 1이 작업중이던 내용을 변경하면 안되기 때문. 그래서 도입된 개념이 critical section 과 lock이다. 공유 데이터를 사용하는 코드 영역을 critical section을 지정해놓고 공유 데이터가 가지고 있는 lock을 획득한 단 하나의 쓰레드만 이 영역 내의 코드를 수행할 수 있게 한다. 수행하던 쓰레드가 해당 영역 내의 코드를 수행하고 벗어나서 lock을 반납해야만 다른쓰레드가 반납된 lock을 획득하여 임계영역의 코드를 수행할 수 있다.
이처럼 한쓰레드가 진행중인 작업을 다른 쓰레드가 간섭하지 못하도록 막는 것을 쓰레드의 동기화라고 한다.