자바를 접하다 보면 항상 따라오는 이야기 중 하나가 바로
스레드에 대한 것이다.
스레드는 동기화, 데드락, 크리티칼 섹션 등 수 많은 고려사항을 갖고 있는지만 막상 본인은 스레드가 뭐고 어렴풋이 알고 (사실 들어만 본 수준) 있기 때문에 이에 대해 어느정도 이해를 하고자 하였다.
나름의 조사를 토대로 작성한 글이나 틀린 내용이 존재할 수 있습니다.
해당 부분에 대해서 주저없는 피드백 부탁드립니다!
스레드(thread)는 어떠한 프로그램 내에서, 특히 프로세스 내에서 실행되는 흐름의 단위를 말한다. 일반적으로 한 프로그램은 하나의 스레드를 가지고 있지만, 프로그램 환경에 따라 둘 이상의 스레드를 동시에 실행할 수 있다. 이러한 실행 방식을 멀티스레드(multithread)라고 한다.
출처 : http://asq.kr/g5ltsB
음 .. 정말 겉핥기 식으로만 알던 문장의 반복이다.
그럼 무얼 알아봐야하나.. 하다가
자바는 JVM이라는 virtual machine에서 돌아간다. 지겹도록 들었던 말이다. 그런데 얘가 thread와 상관이있다니? 살펴보아야겠다.
위는 JVM의 Runtime Data Area를 도식화한 것이다.
각 스레드는 PC, JVM stack, Native Method stack으로 이루어진 자신만의 영역을 가지며 나머지 Method Area, Heap 을 공유한다.
- 어떠한 프로세스든지 해당 프로세스를 실행시키면 OS로 부터 특정 크기의 메모리를 할당 받는다.
- 프로세스는 또한 해당 프로세스에 대한 가상 주소 공간(Virtual address space)을 소유한다.
- Virtual Address Space는 두가지 공간을 갖는다.
-User Space : 각각 프로세스마다 독립적으로 가지고 있는 공간. 유저 프로그램이 저장되는 공간. (Native Memory)
-Kernel Space: Kernel이 존재 하는 공간. 모든 프로세스와 공유하는 공간
출처 : https://mia-dahae.tistory.com/100
How JVM stack, heap and threads are mapped to physical memory or operation system?
- there is no magic. The JVM heap is a region of memory, a JVM stack is the same a native stack which is what C+ uses, the JVM's registers is the same as native registers which is what C+ uses and JVMs thread are actually native threads which is what C+ uses.
누가 스레드 소리를 내었어!
쉽게 갈리가 없다. 스레드도 그 종류가 있다.
사용자 레벨 스레드 (User-Level Thread)
사용자 스레드는 커널 영역의 상위에서 지원되며 일반적으로 사용자 레벨의 라이브러리를 통해 구현되며, 라이브러리는 스레드의 생성 및 스케줄링 등에 관한 관리 기능을 제공한다. 동일한 메모리 영역에서 스레드가 생성 및 관리되므로 속도가 빠른 장점이 있는 반면, 여러 개의 사용자 스레드 중 하나의 스레드가 시스템 호출 등으로 중단되면 나머지 모든 스레드 역시 중단되는 단점이 있다. 이는 커널이 프로세스 내부의 스레드를 인식하지 못하며 해당 프로세스를 대기 상태로 전환시키기 때문이다.
커널 레벨 스레드 (Kernel-Level Thread)
커널 스레드는 운영체제가 지원하는 스레드 기능으로 구현되며, 커널이 스레드의 생성 및 스케줄링 등을 관리한다. 스레드가 시스템 호출 등으로 중단되더라도, 커널은 프로세스 내의 다른 스레드를 중단시키지 않고 계속 실행시켜준다. 다중처리기 환경에서 커널은 여러 개의 스레드를 각각 다른 처리기에 할당할 수 있다. 다만, 사용자 스레드에 비해 생성 및 관리하는 것이 느리다.
뭔소린가!?
굉장히 헛갈린다. 아래를 참조하자
OS-level thread(kernel, native therad) vs Green thread
https://stackoverflow.com/questions/15983872/difference-between-user-level-and-kernel-supported-threads
대략 이해한 바는 이렇다.
user-level thread(자바에서 deprecated 된 green thread)
는 멀티코어의 이점을 살릴 수 없다. 그래서 kernel-level 스레드를 사용하는 것이 일반적이다.
(이 부분 확신이 없습니다..
java에서 말하는 user-level thread = green thread 가 불명확하네요. 혹시 아는 분 있으면 피드백 좀 부탁드려요 ㅠㅠ)
일반적으로 프로그래밍 할 때 new Thread() 하고 부르는 친구는 대체 누구일까? 결론적으로 보자면 OS-level thread(kernel, native therad 이하 OS thread)다.
Java threads are "user" threads, but under the hood, the Java Virtual Machine is using kernel threads and delegating the user threads CPU time on each kernel thread in its kernel thread pool.
(해당 글 귀의 "user" threads는 user-level thread를 뜻하지 않는다. 생성 주체가 user라는 측면에서 이야기한 것 같다.)
출처 : https://stackoverflow.com/questions/18278425/are-java-threads-created-in-user-space-or-kernel-space
The magic happens inside the start method, which invokes a start0 method which is declared as a native method. The ‘native’ marker tells JVM that this is a platform specific native method (written in C/C++) which needs to be invoked through java native interface. JNI is a native method interface specification for Java and it details out how native code can integrate with the JVM and vice versa.
출처 : https://medium.com/@unmeshvjoshi/how-java-thread-maps-to-os-thread-e280a9fb2e06
OS는 C와 C+의 세상이다. OS에게 thread를 할당받기 위해서는 그들의 언어로 이야기 해주어야 한다. JVM은 JNI(java native interface)라는 통역사를 통해 자신에게 thread가 필요하다고 알려준다.
JVM도 결국은 프로세스로 OS로부터 메모리를 할당 받는다.
각 JVM Runtime Data Area에 있는 Thread들은 각자의 영역(PC, JVM stack, native method) 외에 Heap과 Method Area를 공유한다. Heap영역도 결국은 메모리의 특정 부분이며 이 부분에 Thread 들이 참조하며 일어나는 문제점과 고려사항을 알아야하는 것이 포인트였다.
heap과 thread stack영역의 선을 얼마나 잘 긋느냐가 관건 아닐까?
( thread-safe 란 ? )
https://greatleee.github.io/what_is_the_thread_safety/
조사 중에 찾게된 글이다. 답변 중 말을 조금 쎄게 하였지만 thread handling에 관해 한이 서린 내용이 있어 가져와 봤다.
I remember the JVM abandoning green threads and moving to native threads. This was for two simple reasons: the green threads were frankly rubbish, and there was a need to support multi-core processors with the limited developer effort available at Sun.
green thread는 쓰레기다
I've often wondered why multi-threading is so hard in Java but it's now becoming clearer - it was ultimately to do with the switch to native threads, which are:
- good at using all the cpu cores
- good at being truly concurrent, providing independent I/O etc
- slow at context switching (compared with the best green thread implementations)
- horribly greedy with memory, hence limiting the maximum usable number of them
- a poor abstraction for any basis for expressing the real world, which is highly concurrent of course.
출처 :https://softwareengineering.stackexchange.com/questions/120384/why-not-green-threads
답변을 쓴 사람은 많은 개발자들이 non-blocking I/O 진영으로 간다고 한다. 하지만 이 글도 8년 전글이다. 현재는 Spring 에도 WebFlux라는 비동기, non-blocking I/O 방식의 프레임 워크가 등장했다.
(그렇다면 non-blocking I/O 방식이 Multi-threading 방식보다 정말 빠를까?)
https://stackoverflow.com/questions/8546273/is-non-blocking-i-o-really-faster-than-multi-threaded-blocking-i-o-how
The biggest advantage of nonblocking or asynchronous I/O is that your thread can continue its work in parallel. Of course you can achieve this also using an additional thread. As you stated for best overall (system) performance I guess it would be better to use asynchronous I/O and not multiple threads (so reducing thread switching).
thread는 요청을 받아도 non-blocking I/O 처리를 하기 때문에 다른일을 할 수 가 있다.
김Thread 과장이
박I/O 대리에게
김과장 : "김대리 이거 처리해서 완료되는대로 가져다 줘"
박대리 : "....넵()"
김Thread 과장은 위처럼 박I/O 대리에게 일 처리를 최대한 빨리 해서 달라고 한 뒤 다른 일을 진행한다. 김Thread 과장은 박I/O 대리가 일을 모두 마친 후 보고 받으면 이를 최Request 부장에게 보고한다.
Event-driven 방식의 프레임워크
- 그러니까 비동기 방식이 더 빠르다 이거아닙니까!
사실 이 글은 이펙티브 자바의 스레드 관련 아이템에 대한 발표를 진행하기 위해 쓰려고 했였다.
그러나 워낙 깊은 내용과 방대한 자료로 인해 큰 혼돈의 카오스로 빠져버렸다. 해서 이펙티브 자바내용은 따로 정리하는 것으로 나 자신과 타협.
부족한 기본기 탓에 계속 애를 먹고 있다. 글을 쓰면서도 개념에 대해 계속 헛갈리고 용어에 집착하기 시작하였다. 학부생 때 배운내용인 거 같은데.. (과거의 나를 원망하자)
어쨌든 나름의 조사를 통해서 정리 아닌 정리를 해서 자바에서 쓰는 쓰레드는 무엇이고 왜 이렇게 속을 썩이는 걸까? 에 대해 다뤄봤다.
참조링크 위주의 해설이 빈약한 불친절한 글일 수 있지만(사실 다 압니다.) 추후에 계속해 보충할 수 있도록 해보겠다..