본문 바로가기

JAVA

[Thread] 동시성 프로그래밍

동시성 프로그래밍에 대한 교육을 진행중이다. 


모처럼 감기 때문에 몸이 많이 안좋지만 교육이 재밌다보니 맨정신이 아님에도 불구하고 교육장을 찾게 된다.


동시성 프로그래밍


JAVA에서는 thread를 얘기한다. thread는 단어의 정의를 찾아보면 실이라고 나오는데 


그 실이라고 함은 프로그램을 돌려보면 왜 thread로 명명했는지 예상이 가능하다.


스레드는,

동작하고 있는 프로그램을 프로세스(Process)라고 한다. 보통 한 개의 프로세스는 한 가지의 일을 하지만, 이 쓰레드를 이용하면 한 프로세스 내에서 두 가지 또는 그 이상의 일을 동시에 할 수 있게 된다.


thread는 프로세스와는 다르다. 흔히 프로세스는 하나의 주소를 할당받아 특정 프로그램이 그 프로세스를 사용하게 되지만 thread는 하나의 프로세스 자원을 공유하면서 서로 교차하며 작동한다. 스레드간 서로 교차하면서 작동하고 그 교차점이 빠르기 때문에 사용자는 동시에 진행 되는 것처럼 느껴진다. 

하지만 하나의 CPU를 사용하는 것과

두개 이상의 CPU 자원을 사용하는 것은 또 다른 문제다. 

하나의 CPU를 사용한다는 것은 하나의 자원을 공유하면서 쓴다는 것이기 때문에 여러개의 스레드가 서로 교차하면서 실행되는 그림이 예상된다, 하지만 여러개의 CPU를 사용한다면 각각의 자원을 사용하기 때문에 다른 그림이 예상될 것이다. 


자바로 thread를 구현하고자 하면 흔히,

implements Runnble을 구현하거나 extends Thread를 상속받는다.

두가지의 큰 차이점은 일단 Thread를 상속받지 못하는 상황일 경우에 Runnable을 구현함으로서 thread를 구현할 수 있다는 점이다. 

두가지 방법 모드 run 메서드를 overrive를 해야함으로서 실질적으로 스레드를 구현 할 수 있다. 


그러면 흔히 사용하는 방법 중에서 

Runnable을 통해 run()을 실행하는 것과 Thread start()를 통해서 실행하는 것은 어떻게 다를까 ?

우리는 먼저 자바의 메모리 구조부터 생각해보아야 한다. 


자바의 메모리 구조는 힙,메서드,스택 구조로 나뉜다. 

힙은 인스턴스와 된 클래스가 생성되는 부분으로 가비지 컬렉터의 대상이다.

메서드는 모든 스레드가 공유하는 영역으로 각각의 클래스와 인터페이스에 대한 런타임 상수 풀, 필드와 메서드 정보, static 변수, 메서드 바이트 코드를 저장한다.

스택은 스레드가 시작될때 생성되는 공간으로 스레드 마다 각각의 스택이 생성된다. JVM에 스택 프레임을 추가하고 push하고 pop동작만 수행한다.


자바의 main을 통해서 자바 어플리케이션을 실행하면 모든 생성된 모든 스레드는 main이라는 스레드 위에서 작동한다. 

여기서 run()을 통해서 실행한 스레드가 에러가 발생하였을 경우 main 동작에도 영향을 미치지만 

start()를 통해서 실행한 스레드는 스레드마다 각각의 스택을 생성하기 때문에 main에 영향을 미치지 않는다. 이것은 main에서 start()를 통해서 run()을 호출하는 것이 아니라. JVM을 통해 OS 스케쥴러가 run()의 주소를 알고 호출하기 때문에 그러한 것이다. 


run이나 start나 생명주기는 어떠할까.

run으로 수행한 스레드의 생명주기는 해당 스택의 run에서 생명주기가 시작해서 해당 스택에서 생명주기가 끝이 난다.

하지만 start로 시작한 스레드는 start()에서 생명주기가 시작되지만 생명주기가 끝나는 곳은 start를 시작한 stack이 아니라 스레드별로 생성된 스택에서 스레드의 생명주기가 끝이 난다. 

그러면 start()가 실행되었다고 해서 main도 종료되는 것일까 ? 그건 아니다. main은 모든 스레드의 작업이 종료 된 후에 main이 종료된다.


그리고 Thread 또한 생성된 Thread의 재사용은 불가하다. 실행된 Thread는 생명주기가 끝나기 때문에 그러하다.


우선순위에 대해 얘기해본다.

자바로 멀티 스레드를 구현할 시 코딩 순서와 행 순서에 따라서 먼저 작업된 Thread가 먼저 실행 될 것이라는 착각은 버리는 것이 좋다. 리눅스와 같은 운영체제에서도 우선순위를 부여할 순 있지만 부여된 우선순위와 코딩된 순서에 따라서 스레드가 실행되지는 않는다. 먼저 실행되기를 바란다면 특정 thread의 동작이 수행된 이후에 다른 스레드가 작업할 수 있게 개발을 해주어야 한다.