내 개발자 친구가 최근에 다음과 같은 질문을 했습니다. Linux 시스템에서 스레드가 있는 Java 애플리케이션을 실행할 때 해당 스레드가 기본 Linux 운영 체제에 어떻게 표시됩니까?
그렇다면 Java 스레드란 무엇입니까?
답변1
총 길이 DR
존재하다자바 1.1, 녹색 스레드가 사용되는 유일한 스레딩 모델입니다.자바 가상 머신(JVM),9적어도 안으로솔라리스. 녹색 스레드는 기본 스레드에 비해 몇 가지 제한 사항이 있기 때문에 Java의 후속 버전에서는 기본 스레드를 선호하여 녹색 스레드를 삭제했습니다.10,11.
원천:녹색 실
다음 그림은 운영 체제 관점에서 Java 스레드를 분석하는 방법을 보여줍니다.
배경
이 문제를 조사하는 동안 다음과 같은 제목의 질문과 답변을 발견했습니다.Java JVM은 pthread를 사용합니까? . 이 질문에는 JVM 소스 코드에 대한 링크가 있습니다.OpenJDK/jdk8u/jdk8u/핫스팟. 특히 이 단락은 다음과 같습니다.
// Serialize thread creation if we are running with fixed stack LinuxThreads
bool lock = os::Linux::is_LinuxThreads() && !os::Linux::is_floating_stack();
if (lock) {
os::Linux::createThread_lock()->lock_without_safepoint_check();
}
pthread_t tid;
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
pthread_attr_destroy(&attr);
여기서 우리는 JVM이 일명 pthread를 사용하고 있음을 볼 수 있습니다.POSIX 스레드. 추가 세부정보pthreads(7) 매뉴얼 페이지:
POSIX.1은 종종 POSIX 스레드 또는 Pthread라고 불리는 스레드 프로그래밍을 위한 인터페이스(함수, 헤더 파일) 세트를 지정합니다. 단일 프로세스에는 여러 스레드가 포함될 수 있으며 모두 동일한 프로그램을 실행합니다. 이러한 스레드는 동일한 전역 메모리(데이터 및 힙 세그먼트)를 공유하지만 각 스레드에는 자체 스택(자동 변수)이 있습니다.
이러한 이유로 Java에 존재하는 유일한 스레드는 Linux의 pthread입니다.
예
실험이를 더 자세히 증명하기 위해 다음 예를 사용할 수 있습니다.스칼라 앱결국 이것은 단지 Java 애플리케이션일 뿐입니다.
애플리케이션은 Docker 컨테이너에서 실행되지만 이를 사용하여 스레드를 사용하는 실행 중인 Java 애플리케이션을 연구할 수 있습니다. 이 애플리케이션을 사용하려면 Git 저장소를 복제한 다음 Docker 컨테이너를 빌드하고 실행하면 됩니다.
애플리케이션 빌드$ git clone https://github.com/slmingol/jvmthreads.git
$ cd jvmthreads
$ docker build -t threading .
$ docker run -it -v ~/.coursier/cache:/root/.cache/coursier -v ~/.ivy2:/root/.ivy2 -v ~/.sbt:/root/.sbt -v ~/.bintray:/root/.bintray -v $(pwd):/threading threading:latest /bin/bash
이 시점에서 Docker 컨테이너 내부에 있어야 하며 다음 유형의 프롬프트가 나타나야 합니다.
root@27c0fa503da6:/threading#
애플리케이션 실행
여기에서 애플리케이션을 실행하려고 합니다 sbt
.
$ sbt compile compileCpp "runMain com.threading.ThreadingApp"
Ctrl이 애플리케이션이 실행되기 시작하면 애플리케이션에서 +를 사용하여 연구할 수 있습니다.ZSIGSTP
root@27c0fa503da6:/threading# sbt compile compileCpp "runMain com.threading.ThreadingApp"
[info] Loading settings from metaplugins.sbt ...
[info] Loading project definition from /threading/project/project
[info] Loading settings from plugins.sbt ...
[info] Loading project definition from /threading/project
[info] Loading settings from build.sbt ...
[warn] Missing bintray credentials. Either create a credentials file with the bintrayChangeCredentials task, set the BINTRAY_USER and BINTRAY_PASS environment variables or pass bintray.user and bintray.pass properties to sbt.
[warn] Missing bintray credentials. Either create a credentials file with the bintrayChangeCredentials task, set the BINTRAY_USER and BINTRAY_PASS environment variables or pass bintray.user and bintray.pass properties to sbt.
^Z
[1]+ Stopped sbt compile compileCpp "runMain com.threading.ThreadingApp"
분석 애플리케이션
ps
이제 여기에서 테스트 중인 애플리케이션이 운영 체제 관점에서 어떻게 작동하는지 확인하는 등 일반적인 UNIX 도구를 사용할 수 있습니다 .
표준 ps
cmd에는 실행 중인 일반적인 응용 프로그램만 표시됩니다.
root@27c0fa503da6:/threading# ps -eaf
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 02:14 pts/0 00:00:00 /bin/bash
root 1503 1 0 02:37 pts/0 00:00:00 bash /usr/bin/sbt compile compileCpp runMain com.threading.ThreadingApp
root 1571 1503 98 02:37 pts/0 00:00:35 java -Xms1024m -Xmx1024m -XX:ReservedCodeCacheSize=128m -XX:MaxMetaspaceSize=256m -jar /usr/share/sbt/bin/sbt-launch.jar
root 1707 1571 0 02:37 pts/0 00:00:00 git describe --tags --abbrev=8 --match v[0-9]* --always --dirty=+20191026-0237
root 1718 1 0 02:37 pts/0 00:00:00 ps -eaf
그러나 ps
스레드 보기를 사용하면 더 완전한 그림을 볼 수 있습니다.
root@27c0fa503da6:/threading# ps -eLf | head -8
UID PID PPID LWP C NLWP STIME TTY TIME CMD
root 1 0 1 0 1 02:14 pts/0 00:00:00 /bin/bash
root 1943 1 1943 0 1 03:08 pts/0 00:00:00 bash /usr/bin/sbt compile compileCpp runMain com.threading.ThreadingApp
root 2011 1943 2011 0 32 03:08 pts/0 00:00:00 java -Xms1024m -Xmx1024m -XX:ReservedCodeCacheSize=128m -XX:MaxMetaspaceSize=256m -jar /usr/share/sbt/bin/sbt-launch.jar compile compileCpp runMain com.threading.ThreadingApp
root 2011 1943 2012 0 32 03:08 pts/0 00:00:05 java -Xms1024m -Xmx1024m -XX:ReservedCodeCacheSize=128m -XX:MaxMetaspaceSize=256m -jar /usr/share/sbt/bin/sbt-launch.jar compile compileCpp runMain com.threading.ThreadingApp
root 2011 1943 2013 0 32 03:08 pts/0 00:00:00 java -Xms1024m -Xmx1024m -XX:ReservedCodeCacheSize=128m -XX:MaxMetaspaceSize=256m -jar /usr/share/sbt/bin/sbt-launch.jar compile compileCpp runMain com.threading.ThreadingApp
root 2011 1943 2014 0 32 03:08 pts/0 00:00:00 java -Xms1024m -Xmx1024m -XX:ReservedCodeCacheSize=128m -XX:MaxMetaspaceSize=256m -jar /usr/share/sbt/bin/sbt-launch.jar compile compileCpp runMain com.threading.ThreadingApp
root 2011 1943 2015 0 32 03:08 pts/0 00:00:00 java -Xms1024m -Xmx1024m -XX:ReservedCodeCacheSize=128m -XX:MaxMetaspaceSize=256m -jar /usr/share/sbt/bin/sbt-launch.jar compile compileCpp runMain com.threading.ThreadingApp
노트:위에서 우리는 많은 스레드가 있음을 볼 수 있습니다. 이 보기에서 관심 있는 열은 LWP
및 입니다 NLWP
.
LWP
경량 프로세스 스레드를 나타냅니다.NLWP
LWP 수량을 나타냅니다.
NLWP 숫자는 PID와 관련된 총 스레드 수를 알려주기 때문에 중요합니다. 이 예에서 해당 숫자는 32입니다. 다음과 같이 확인할 수 있습니다.
root@27c0fa503da6:/threading# ps -eLf|grep -E "[3]2.*java" | wc -l
32
ps
다음 명령을 사용하여 이러한 스레드를 확인하는 다른 방법을 얻을 수도 있습니다.
root@27c0fa503da6:/threading# ps -Lo pid,lwp,pri,nice,start,stat,bsdtime,cmd,comm | head -5
PID LWP PRI NI STARTED STAT TIME CMD COMMAND
1 1 19 0 02:14:42 Ss 0:00 /bin/bash bash
1943 1943 19 0 03:08:41 T 0:00 bash /usr/bin/sbt compile c bash
2011 2011 19 0 03:08:41 Tl 0:00 java -Xms1024m -Xmx1024m -X java
2011 2012 19 0 03:08:41 Tl 0:05 java -Xms1024m -Xmx1024m -X java
참고 1:l
STAT 열로 인해 이 형식은 이것이 pthread임을 보여줍니다.
S
- 중단 가능한 절전 모드(이벤트가 완료될 때까지 대기)T
- 작업 제어 신호에 의해 중지됨l
- 다중 스레드입니다(NPTL pthread와 마찬가지로 CLONE_THREAD 사용).
노트 2:SIGSTP 제어 신호 +를 사용하여 프로세스가 중지 되었음을 나타내기 S
때문에 여기서 중요합니다 .T
CtrlZ
ps -T
스위치를 사용하여 스레드로 처리 할 수도 있습니다 .
root@27c0fa503da6:/threading# ps -To pid,tid,tgid,tty,time,comm | head -5
PID TID TGID TT TIME COMMAND
1 1 1 pts/0 00:00:00 bash
1943 1943 1943 pts/0 00:00:00 bash
2011 2011 2011 pts/0 00:00:00 java
2011 2012 2011 pts/0 00:00:05 java
위의 스위치 ps
:
-L Show threads, possibly with LWP and NLWP columns.
-T Show threads, possibly with SPID column.
앱을 완전히 실행하세요.
참조 목적으로 궁금하신 경우 Scala/Java 애플리케이션의 전체 실행을 확인하세요.
root@27c0fa503da6:/threading# sbt compile compileCpp "runMain com.threading.ThreadingApp"
[info] Loading settings from build.sbt ...
[warn] Missing bintray credentials. Either create a credentials file with the bintrayChangeCredentials task, set the BINTRAY_USER and BINTRAY_PASS environment variables or pass bintray.user and bintray.pass properties to sbt.
[warn] Missing bintray credentials. Either create a credentials file with the bintrayChangeCredentials task, set the BINTRAY_USER and BINTRAY_PASS environment variables or pass bintray.user and bintray.pass properties to sbt.
[info] Set current project to threading (in build file:/threading/)
[info] Executing in batch mode. For better performance use sbt's shell
[warn] Credentials file /root/.bintray/.credentials does not exist, ignoring it
[success] Total time: 2 s, completed Oct 26, 2019 4:11:38 AM
[success] Total time: 1 s, completed Oct 26, 2019 4:11:39 AM
[warn] Credentials file /root/.bintray/.credentials does not exist, ignoring it
[info] Running (fork) com.threading.ThreadingApp
[info] Started a linux thread 140709608359680!
[info] Started a linux thread 140709599966976!
[info] Starting thread_entry_pointStarted a linux thread 140709591574272!
[info] Starting thread_entry_pointStarting thread_entry_pointStarted a linux thread 140709583181568!
[info] Running Thread 1
[info] Starting thread_entry_pointStarted a linux thread 140709369739008!
[info] Running Thread 2
[info] Starting thread_entry_pointStarted a linux thread 140709608359680!
[info] Running Thread 3
[info] Starting thread_entry_pointStarted a linux thread 140709599966976!
[info] Running Thread 4
[info] Running Thread 5Starting thread_entry_pointStarting thread_entry_pointStarted a linux thread 140709361346304!
[info] Running Thread 6
[info] Starting thread_entry_pointStarted a linux thread 140709583181568!
[info] Started a linux thread 140709591574272!
[info] Starting thread_entry_pointStarting thread_entry_pointStarted a linux thread 140709352953600!
[info] Running Thread 7
[info] Running Thread 9
[info] Started a linux thread 140709369739008!
[info] Starting thread_entry_pointStarted a linux thread 140709608359680!
[info] Running Thread 8
[info] Starting thread_entry_pointStarted a linux thread 140709344560896!
[info] Starting thread_entry_pointStarted a linux thread 140709583181568!
[info] Starting thread_entry_pointStarted a linux thread 140709599966976!
[info] Starting thread_entry_pointStarted a linux thread 140709336168192!
[info] Running Thread 10
[info] Running Thread 11
[info] Starting thread_entry_pointStarted a linux thread 140709327775488!
[info] Running Thread 12Started a linux thread 140709591574272!
[info] Running Thread 13
[info] Running Thread 14
[info] Running Thread 16
[info] Running Thread 15
[info] Running Thread 18
[info] Running Thread 17
[info] Running Thread 19
[info] Starting thread_entry_pointStarting thread_entry_point
[success] Total time: 1 s, completed Oct 26, 2019 4:11:40 AM
스레드 덤프?
어떤 사람들은 Java 스레드를 Linux LWP와 어떻게 연관시키는지 묻습니다. 이를 위해 Java 스레드 덤프를 사용하여 2를 비교할 수 있습니다.
Ctrl다시 한 번 위와 동일한 Scala 애플리케이션을 사용하고 + 로 이동합니다 Z.
root@52a4b6e78711:/threading# sbt compile compileCpp "runMain com.threading.ThreadingApp"
[info] Loading settings from metaplugins.sbt ...
[info] Loading project definition from /threading/project/project
^Z
[1]+ Stopped sbt compile compileCpp "runMain com.threading.ThreadingApp"
이 작업이 완료되면 SIGQUIT를 JVM에 보내야 합니다. 이렇게 하려면 일반적으로 다음을 사용할 수 있습니다 kill -3 <PID of JVM>
.
root@52a4b6e78711:/threading# ps -eaf
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 12:36 pts/0 00:00:00 /bin/bash
root 7 1 0 12:37 pts/0 00:00:00 bash /usr/bin/sbt compile compileCpp runMain com.threading.ThreadingApp
root 75 7 99 12:37 pts/0 00:00:17 java -Xms1024m -Xmx1024m -XX:ReservedCodeCacheSize=128m -XX:MaxMetaspaceSize=256m -jar /usr/share/sbt/bin/sbt-launch.jar
root 130 1 0 12:37 pts/0 00:00:00 ps -eaf
root@52a4b6e78711:/threading# kill -3 75
그런 다음 프로그램이 다시 시작되도록 허용해야 합니다 fg
.
root@52a4b6e78711:/threading# fg
sbt compile compileCpp "runMain com.threading.ThreadingApp"
2019-10-26 12:38:00
Full thread dump OpenJDK 64-Bit Server VM (25.181-b13 mixed mode):
"scala-execution-context-global-32" #32 daemon prio=5 os_prio=0 tid=0x00007f87d8002800 nid=0x80 runnable [0x00007f880973d000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000c1b08a68> (a scala.concurrent.impl.ExecutionContextImpl$$anon$3)
at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
"scala-execution-context-global-33" #33 daemon prio=5 os_prio=0 tid=0x00007f87dc001000 nid=0x7f runnable [0x00007f880983e000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000c1b08a68> (a scala.concurrent.impl.ExecutionContextImpl$$anon$3)
at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
"scala-execution-context-global-31" #31 daemon prio=5 os_prio=0 tid=0x00007f87d8001000 nid=0x7e waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"scala-execution-context-global-30" #30 daemon prio=5 os_prio=0 tid=0x00007f87e4003800 nid=0x7d waiting on condition [0x00007f8809a40000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000c1b08a68> (a scala.concurrent.impl.ExecutionContextImpl$$anon$3)
at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
노트:위 명령의 부분 출력에서 kill -3
JVM의 스레드가 분석 없이 정렬되어 있는 것을 볼 수 있습니다. 그 중 32개가 있는데, 이는 Java 스레드가 실제로 Linux LWP와 1:1임을 보여줍니다.