- 백기선-JAVA STUDY/WEEK102021년 02월 21일 12시 17분 55초에 업로드 된 글입니다.작성자: jCurve728x90반응형
목표
자바의 멀티쓰레드 프로그래밍에 대해 학습하세요.
학습할 것 (필수)
- Thread 클래스와 Runnable 인터페이스
- 쓰레드의 상태
- 쓰레드의 우선순위
- Main 쓰레드
- 동기화
- 데드락
Thread 클래스와 Runnable 인터페이스
우선 프로세스와 스레드의 의미를 정리해햐 할 것 같다.
우리가 크롬과 같은 프로그램을 실행하면 프로세서가 프로세스를 실행시키는데 프로세스는 1개 이상의 스레드와 자원을 할당하는 단위로 생각하면 되고 내부 스레드가 동작하며 실제 작업을 수행한다.
두 개 이상의 스레드로 작업을 실행하는 프로세스를 멀티 스레드 프로세스라고 하며, 프로세스의 할당된 자원을 스레드가 공유하며 사용하지만 프로세스간에는 할당된 자원이 아예 다르기 때문에 공유하지 않는다.
추가적으로 멀티 태스킹은 여러 개의 프로세스가 동시에 실행됨을 의미하며 대부분의 os에서 지원해 준다.
동시성과 병렬성의 차이를 간단한 비유로 이해해보자면 동시성은 스레드가 여러가지 일을 번갈아 가며하는 것이고 병렬성은 멀티 코어에서 여러가지 프로세스를 진짜 말 그대로인 "동시에" 실행하는 것인데 말 보다 비유가 더 쉽다.
동시성은 책에서 2줄 읽고 → 잠시 누워서 3초 자고 → 밥 한 숟갈 뜨고 → 다시 책 2줄 읽고 이런식이면
병렬성은 밥 먹으면서 넷플릭스를 보는 거라고 생각하면 이해가 쉽다.
Thread클래스는 Runnable 인터페이스를 구현한 클래스로 실제 사용에서 어떤 것을 적용하냐의 차이이다.
- 쓰레드에서 run을 제외한 다른 것들을 오버라이딩 할 필요가 있는 경우 Thread를 상속받아 구현하고
- Thread 클래스는 다양한 메소드들이 존재하며 Thread를 상속 받으면 다양한 메소드들을 원하는대로 오버라이딩하여 구현할 수 있다.
- 쓰레드에서 run만 정의한다면 Runnable을 구현하면 된다.
Thread thread = new Thread(Runnable target);
Thread 클래스를 직접 구현할 시 Runnable을 매개값으로 갖는 생성자를 호출해야 한다.
Runnable의 구현 클래스는
class Task implements Runnalbe{ @Override public void run(){ //스레드가 실행할 코드 ... } }
이런식으로 구현하면 된다.
혹은 Runnable을 익명객체로 구현하여 더 짧게 사용할 수 있다.
Thread thread = new Thread(()->{//스레드가 실행할 코드 });
또한 Runnable은 run()메소드 하나만 정의되어 있는 함수형 인터페이스 이므로 람다식으로 매개값을 사용할 수 있다.
Thread thread = new Thread(()->{//스레드가 실행할 코드 });
스레드는 생성 후 자동실행되지 않기 때문에 start()메소드를 실행 시켜 주어야한다.
스레드의start()메소드를 실행시켜주면 앞서 구현했던 run()메소드 내부에 스레드가 동작해야할 일을 명시해둔 부분이 실행되는 구조이다.
스레드의 상태
- NEW
- 스레드가 생성되고 아직 start()가 호출되지 않은 상태
- RUNNABLE
- 실행 중 또는 실행 가능한 상태
- BLOCKED
- 동기화 블럭에 의해 일시정지된 상태(lock이 풀리길 기다리는 상태)
- WAITING,TIMED_WATITING
- 스레드의 작업이 종료되지 않았지만 실행 가능하지 않는 일시정지 상태로 WAITING은 일시정지 시간이 지정된 경우이다.
- TERMINATED
- 스레드의 작업이 종료된 상태
스레드의 상태를 제어할 수 있는 메서드도 존재한다.
메서드 설명 static void sleep(long millis)static void sleep(long millis, int nanos) 지정된 시간(밀리세컨드, or 나노세컨드)동안 쓰레드를 일시정지시키다. 지정한 시간이 지나고 나면, 자동적으로 다시 실행대기 상태가 된다. void join()void join(long millis)void join(long millis, int nanos) 지정된 시간동안 쓰레드가 실행되도록 한다. join()을 호출한 쓰레드는 그동안 일시정지 상태가 된다. 지정된 시간이 지나거나 작업이 종료되면 join()을 호출한 쓰레드로 다시 돌아와 실행을 계속한다. void interrupt() sleep()이나 join()에 의해 일시정지 상태인 쓰레드를 깨워서 실행대기 상태로 만든다. 해당 쓰레드에서는 InterruptedException이 발생함으로써 일시정지 상태를 벗어나게 된다. void stop() 쓰레드를 즉시 종료시킨다. void suspend() 쓰레드를 일시정지시킨다. resume()을 호출하면 다시 실행대기 상태가 된다. void resume() suspend()에 의해 일시정지 상태에 있는 쓰레드를 실행대기 상태로 만든다. static void yield() 실행 중에 자신에게 주어진 실행시간을 다른 쓰레드에게 양보하고 자신은 실행대기 상태가 된다. resume(),stop(),suspend()는 스레드를 교착상태로 만들기 쉽기 때문에 deprecated되었다.
왜 교착 생태로 만들기 쉽냐 하면 run()의 작업을 하고 있는 스레드는 상태가 TERMINATED가 되어야 작업 완료후 프로세스로 자원 반납까지 깔끕하게 끝내는데 도중에 멈춰버리고 작업을 중단시키는 행위 자체는 작업을 중단하지만 자원을 반납하지 않아서 dead-lock이 걸리기 쉬운 것이다 ^^
스레드 우선순위
스레드는 1~10 (낮음~높음) 순위의 우선순위를 나타내는 멤버 변수를 가지고 있으며 이 값을 메서드를 통해 개발자가 직접 지정해줄 수 있다.(default는 5이다.)
public class Thread implements Runnable{ void setPriority(int newPriority) int getPriority() public static final int MIN_PRIORITY = 1; public static final int NORM_PRIORITY = 5; public static final int MAX_PRIORITY = 10;
위와 같이 Thread클래스에 우선순위와 관련한 메서드와 상수가 정의되어 있다.
여기서 한 가지 착각할 수 있는데 스레드 우선위에 따라 절대적으로 더 많은 기회와 시간으로 실행 기회를 결정하는 것이 아니라 OS의 스케쥴링 정책과 JVM 구현에 따라 실제 작업은 달라질 수 있다 (우선순위 지정은 그냥 나의 바램을 전달하는 정도?)
Main 스레드
main 스레드는 우리가 처음 자바 프로젝트를 생성하여 main()메소드를 시작하는 부분이라고 할 수 있다
public class Study{ public static void main(Stirng[] args){ //... } }
main () 내부에 새로운 스레드를 만들어서 멀티 스레드를 구현할 수 있으며, 싱글 스레드 애플리케이션에서는 메인 스레드가 종료되면 프로세스도 종료되지만 멀티 스레드 애플리케이션은 진행중인 스레드가 하나라도 있다면 종료되지 않는다.
동기화
멀티 스레딩으로 작업을 진행하게 되면 여러개의 스레드에서 한 개의 객체에 접근하게 되는 일이 생기는데 그렇게되면 여러 스레드에서 공유 객체의 상태를 지속적으로 변화 시키기 때문에 원하지 않는 결과가 도출될 수 있으며 그 결과를 예상하기도 힘들다.
따라서 이러한 객체를 공유할 일이 있으면 다른 스레드를 일시 정지 시키고 작업이 끝난 후 일시 정지를 풀고 해당 객체를 다른 스레드가 작업을 이어가는 방식으로 사용하는데 이때 synchronized 키워드를 사용한다.
(이렇게 한 개의 스레드만 실행할 수 있는 코드 영역을 critical section 이라고 한다)
데드락
위에서 설명하다 잠깐 데드락에 대해 언급했는데 여기서 좀 더 자세히 살펴 보자
DeadLock(교착상태)는 둘 이상의 쓰레드가 lock을 획득하기 위해 대기하는데, 이 lock을 가지고 있는 쓰레드들도 똑같이 다른 lock을 기다리면서 서로 block 상태에 놓이는 것을 말한다.
즉, 다수의 쓰레드가 같은 lock을 동시에, 다른 명령을 통해 획득하려할 때 발생할 수 있다.
데드락은 한 시스템 내에서 다음의 네 가지 조건이 동시에 성립할 때 발생한다.
-
상호 배제(Mutual exclusion)
자원은 한 번에 한 프로세스만이 사용할 수 있어야 한다.
-
점유 대기(Hold and wait)
최소한 하나의 자원을 점유하고 있으면서 다른 프로세스에 할당되어 사용하고 있는 자원을 추가로 점유하기 위해 대기하는 프로세스가 있어야 한다.
-
비선점(No preemption)
다른 프로세스에 할당된 자원은 사용이 끝날 때까지 강제로 빼앗을 수 없어야 한다.
-
순환 대기(Circular wait)
프로세스의 집합{P0,P1,P2...Pn}에서 P0는 P1이 점유한 자원을 대기하고 P1은 P2가 점유한 자원을 대기하고 P2..Pn-1은 Pn이 점유한 자원을 대기하며 Pn은 P0이 점유한 자원을 요구해야 한다.
교착 상태 handling
-
교착 상태 예방
교착상틔 필요조건을 부정함으로 써 교착상태가 발생하지 않도록 미리 예방하는 방법
-
교착상태 회피
교착상태 가능성을 배제하지 않고 적절하게 피해나가는 방법
-
교착상태 탐지
교착상태 발생을 허용하고 발생 시 원인을 규명하여 해결하는 방법
-
교착상태 복구
교착상태 발견 후 환형대기를 배제시키거나 자원을 중단하는 메모리 할당 기법
반응형'JAVA' 카테고리의 다른 글
백기선 - JAVA STUDY/WEEK12 (0) 2021.03.06 백기선-JAVA STUDY/WEEK11 (0) 2021.02.21 백기선-JAVA STUDY/WEEK9 (0) 2021.01.29 백기선-JAVA STUDY/WEEK8 (0) 2021.01.29 백기선 - JAVA STUDY/WEEK7 (0) 2021.01.15 댓글