Thread object 관리
관리 방법
1. 생성 후 종료될 때 자동 삭제 되도록 하기
- Thread는 Runnable interface의 run() 수행이 끝나면 종료되는데, 일정한 작업을 외부 간섭 없이 수행후 종료되면 운영상에 크게 문제가 없다
- 단 이경우 thread를 원하는 시점에 종료시키거나 관련 정보 확인이 어려움
- 구현 되는 클래스 내에서 Thread object를 포함시켜 관리
- Rubbale interface를 구현하는 class가 필요로 하는 Thread instance를 클래스 내에 포함시켜 관리하는데,
- 이 경우 생성된 object에서 자신과 관련된 Thread instance를 관리하기에, thread 제어와 관련된 처리가 가능
- Thread pool 이용
= 1번 방법과 유사하지만, Thread 생성과 삭제를 반복하는 것이 아닌 생성된 Thread instance를 활용하기에 자원 활용면에서 좋다
- Thread Pool은 스레드 관리를 대신해주지만, 필요에 따라 특정 작업의 취소나 상태 확인 등은 가능하지만, Thread 관리에 대한 권한을 직접적으로 가지고 있지 못하므로 Thread 제어가 필요한 경우 사용이 제한될 수 있다
Thread class와 Runnable 인터페이스 이용 차이
class 확장
- 다중 상속을 지원하지 않기에, 다른 class로 부터 추가적인 확장이 불가능
- instance 생성 후 바로 실행할 수 있다
- 간단한 class여도 별도의 class 정의가 필요
Interface 구현
- interface의 경우 여러개를 사용할 수 있기에 구현된 후에도, 해당 class의 확장이 가능
- 추가적인 Thread object가 있어야 한다(Instance 생성 후 바로 사용이 불가능)
- Runnable interface는 functional interface로 Lambda로 구현 가능
Thread 멈추기
Java Thread는 start()에 의해 시작 되지만 종료에 대한 명령은 없다.
초기엔 stop()메소드를 지원 했지만, thread가 실행중에 강제 종료시 thread 내부에서 리소스 정리를 제대로 할 수 없게 되며, lock을 해제 하지 않은 채 thread를 종료시켜 다른 thread에서 lock 획득을 위해 무한히 기다리는 deadlock 상태에 빠질 수 있다.
따라서 Thread를 안전하게 종료하기 위해서 thread내에서 확인만 가능하게 상태를 전달해 스스로 종료할 수 있도록 만들어야 한다.
Interrupt
상태 제어를 통해 thread를 중지 시키려할 때 위와 같은 문제가 발생하는데, 이를 해결하기 위해서는 Java Thread 클래스에서 지원하는 sleep, wait 상태일 때 외부로부터 이벤트를 전달 받을 수 있는 Interrup가 있다.
Thread Class 상태 정보로도 사용되지만, sleep || wait 같은 대기 상태에서 exception을 발생시킨다.
Thread 동시성 제어
Thread 동기화 문제
1. Race Condition(경쟁 조건)
- 둘 이상의 thread가 동시에 공유 자원 접근시 발생
- 동시 접근함으로써 문제가 발생할 수 있는 구역을 Critical Section(임계 구역)
이라 함
- 1번 Thread가 변수를 읽고, 2번 Thread도 변수에서 동일한 값을 읽는다.
- 이후 1,2번 Thread는 각 작업을 수행 후 변수에 마지막으로 값을 쓰기 위해 서로 경쟁하게 되고, 결국 마지막에 덮어쓴 thread 값이 저장 되어 원하는 결과를 얻지 못한다.
2. Critical Section(임계 구역)
- 두 스레드가 동시 접근하여 허용되지 않는 공유 자원에 접근하는 코드의 블록
- Critical Section은 Thread에서 작업에 필요한 최소한의 시간만 유지되어야 하며, 작업이 완료된 후에는 반드시 해제 되어야함
- 따라서 한 thread가 critical section에 들어가 작업이 진행중이라며, 나머지 thread들은 해당 작업 완료까지 대기해야 한다.
- Mutual Exclusion(상호배제)를 보장받기 위해서, critical section에 들어가거나, 나올 때를 위한 다양한 동시성 제어 메커니즘 제공
3. Mutual Exclusion(상호 배제)
- 두개 이상의 process or thread가 동시에 하나의 공유 자원으로 발생할 수 있는 race condition 문제를 해결하기 위해 어느 시점에서의 공유 자원 접근을 하나의 process 혹은 thread로 제한하는 것
4. Deadlock(교착 상태)
mutual exclusion 과정에서 자원 접근 권한 획득과 자원 접근 권한 반환 관계의 꼬임으로 발생
Hold and wait (점유 대기)
- process 2가 resource 1의 접근 권한을 획득한 상태로 resource2의 접근 권한을 기다리는 상태
- process2의 수행 과정이 resource 2의 접근 권한을 획득하여 처리 한 후 resource 1의 접근권한을 해제하면 process 3이 접근 권한을 해제하기 전까지 무한 대기 상태에 놓이게 됨
- 따라서 resource 1의 접근 권한을 요청하는 process 1도 무한대기 상태에 빠짐
Circular Wait(순환 대기)
순환 대기의 경우
Process1 ⬅️ Resource2
⬇️ ⬆️
Resource1 ➡️ Process2
- process 1은 Resource2에 대한 접근 권한을 가진 상태에서 Resource1에 대한 접근 권한을 기다림
- process 2는 Resource1에 대한 접근권한을 가진 상태에서 Resource2에 대한 접근 권한을 기다림
두개의 프로세스는 서로가 다른 process가 가지고 있는 접근 권한을 얻기 위해 대기하고 있어, 하나의 프로세스가 먼저 해제하지 않는 이상 대기 상태는 계속 유지됨
Starvation(기아 상태)
다른 process || thread가 공유 자원의 접근 권한을 지속적으로 가짐으로써, 우선 순위가 낮은 process || thread는 접근 권한을 획득할만큼의 수행 시간을 갖지 못해 무한 대기 상태에 놓일 수 있다.
5. Livelock
-
deadlock 문제 해결을 위해 공유 자원 접근 요청 후 일정 시간 안에 권한 획득에 실패시 수행 과정을 종료하며 발생한다.
-
두개의 process나 thread에서 교착 상태를 유지하다가 일정 시간 후 자원 접근 요청을 철회시, 두개의 process || thread가 동시에 수행하여 자신이 확보하고 있던 공유 자원 접근 권한을 반환해 교착 상태를 해결
-
이경우 교착 상태처럼 아무런 작업도 하지 못하는 것은 아니지만, 해당 자원에 대한 접근 권한을 획득하지 못하므로 관련된 작업을 수행할 수 없다.
-
즉 교착 상태는 관련 process || thread가 대기 상태를 계속 유지함으로써 타 작업 수행이 불가능하지만, livelock의 경우 해당 자원에 대한 처리만 못하고 나머지 작업은 처리되는 차이가 있다.