JVM线程与Thread实例之间的关系

huangapple go评论91阅读模式
英文:

relation between JVM threads and Thread instances

问题

我认为我对操作系统线程和JVM线程之间的映射有了一些了解(首先我们有了"绿色线程",其中1个OS对应多个绿色线程,然后有了1:1映射,现在有了jep-425,我们有了虚拟线程和M:N映射:如果我理解错误,请纠正我)。不过,我对JVM线程和Thread实例之间的映射感到相当困惑:我几乎可以确定start()方法启动的Thread实例与JVM线程之间存在1:1的映射(同样,如果我理解错误,请纠正我),但我不确定当Thread实例被构造时是否会创建JVM线程并分配其堆栈,或者只有在调用start()方法时才会这样做。无论正确答案是什么,我还想知道这是JVM/Java语言规范的一部分还是由实现决定,比如OpenJDK。

英文:

I think I have and understanding of a mapping between OS threads and JVM threads (first we had "green threads" with 1os:Many-green, then there was 1:1 mapping and now with jep-425 we have virtual threads with M:N mapping: please correct me if I'm wrong). I'm however quite confused about the mapping between JVM threads and Thread instances: I'm almost sure that there is a 1:1 mapping between start()-ed Thread instances and JVM threads (again, please correct me if I'm wrong), but I'm not sure if a JVM thread is created and its stack allocated when a Thread instance is constructed or only when start() method is called. Whatever the correct answer is, I also wonder if it is a part of the JVM/Java-language spec or up to implementation, such as OpenJDK.

答案1

得分: 3

以下是您要翻译的部分:

"有一件事可以尝试,通过创建许多 Thread 对象,然后尝试启动所有这些 Thread:"

public class ThreadRunner {
    public static void main(String[] args) {
        int maxThreads = 10_000_000;

        // 创建 Thread 实例
        Thread[] threads = new Thread[maxThreads];
        Runnable r = () -> {
            try {
                Thread.sleep(1000_000);
            } catch (InterruptedException e) {
            }
        };
        for (int i = 0; i < maxThreads; i++) {
            threads[i] = new Thread(r);
        }

        // 启动线程
        int count = 0;
        try {
            for (Thread t : threads) {
                t.start();
                count++;
                if (count % 1000 == 0) {
                    System.out.println(count);
                }
                Thread.yield();
            }
        } catch (OutOfMemoryError e) {
            System.out.println(count);
        }
    }
}

"另一种方法是检查 OpenJDK 的源代码:"

  • Thread.java 调用本地的 start0() 方法

  • Thread.c 声明 start0 被转发到本地的 JVM_StartThread
    > {"start0", "()V", (void *)&JVM_StartThread},

  • jvm.cpp 包含了这个本地函数,该函数确定本地线程的堆栈大小,然后使用以下方式创建它:

      jlong size =
             java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
      // 分配 C++ Thread 结构并创建本地线程。从 java 获取的堆栈大小是 64 位有符号的,
      // 但构造函数接受 size_t(无符号类型),这取决于平台可能是 32 位或 64 位。
      // - 如果 size 大于 UINT_MAX,则在 32 位平台上避免截断。
      // - 避免传递负值,这将导致非常大的堆栈。
      NOT_LP64(if (size > SIZE_MAX) size = SIZE_MAX;)
      size_t sz = size > 0 ? (size_t) size : 0;
      native_thread = new JavaThread(&thread_entry, sz);

"这是否取决于特定的 JVM 实现?"

原则上,这可能取决于特定的 JVM 实现。然而,在真正需要之前创建操作系统线程是不明智的。

我之所以称之为不明智的主要原因是资源管理。当您创建一个操作系统线程时,您必须在不再需要时销毁它。当您调用 start() 一个 Thread 时,操作系统线程被创建,执行线程的 run() 方法,然后在该方法返回后销毁操作系统线程。

如果一个假设的 JVM 实现在 Thread 构造函数中已经创建了操作系统线程,那么它还需要一种方式在 Thread 实例被垃圾回收时销毁该操作系统线程,这将是麻烦的。

英文:

For one thing you could try this for yourself by creating many Thread objects and later try to start all those Threads:

public class ThreadRunner {
    public static void main(String[] args) {
        int maxThreads = 10_000_000;

        // Create Thread instances
        Thread[] threads = new Thread[maxThreads];
        Runnable r = () -&gt; {
            try {
                Thread.sleep(1000_000);
            } catch (InterruptedException e) {
            }
        };
        for (int i = 0; i &lt; maxThreads; i++) {
            threads[i] = new Thread(r);
        }

        // start Threads
        int count = 0;
        try {
            for (Thread t : threads) {
                t.start();
                count++;
                if (count % 1000 == 0) {
                    System.out.println(count);
                }
                Thread.yield();
            }
        } catch (OutOfMemoryError e) {
            System.out.println(count);
        }
    }
}

The other way is to check the source of the OpenJDK:

  • Thread.java calls the native start0() method

  • Thread.c declares that start0 is forwared to the native JVM_StartThread
    > {"start0", "()V", (void *)&JVM_StartThread},

  • jvm.cpp contains that native function which determines the stack size for the native thread and then creates it with

        jlong size =
               java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
        // Allocate the C++ Thread structure and create the native thread.  The
        // stack size retrieved from java is 64-bit signed, but the constructor takes
        // size_t (an unsigned type), which may be 32 or 64-bit depending on the platform.
        //  - Avoid truncating on 32-bit platforms if size is greater than UINT_MAX.
        //  - Avoid passing negative values which would result in really large stacks.
        NOT_LP64(if (size &gt; SIZE_MAX) size = SIZE_MAX;)
        size_t sz = size &gt; 0 ? (size_t) size : 0;
        native_thread = new JavaThread(&amp;thread_entry, sz);
    

Does this depend on the specific JVM implementation?

In principle, it could depend on the specific JVM implementation. It would however be silly to create an OS thread before it is really needed.

The main reason why I call this silly is the resource management. When you create an OS thread you must also destroy it. When you start() a Thread the OS thread is created, the threads run() method is exeucted and after that method returns the OS thread will be destroyed.

If a hypothetical JVM implementation created the OS thread already in the Thread constructor it would also need some way of destroying that OS thread when the Thread instance is garbage collected which will be hassle.

答案2

得分: 0

A Thread is created only when the start() method is called.
You can check it out on the JDK doc here:
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Thread.html

The following code shows you how this works

import java.util.Random;
public class Threads {

    private static final Random rnd = new Random();

    private static int rand(int a, int b) {
        synchronized (rnd) {
            return rnd.nextInt(b - a) + a;
        }
    }

    private static void loop() {
        while (true) {
            synchronized (rnd) {
                try {
                    Thread.sleep(rand(200, 1000));
                } catch (InterruptedException e) {}

                System.out.println("Hi, i'm ");
                System.out.println("thread #" + Thread.currentThread().getId());
            }
        }
    }

    public static class MyThread extends Thread {
        @Override
        public void run() {
            loop();
        }
    }

    public static void main(String[] args) {
        Thread t1 = new MyThread();
        t1.start();

        new Thread(Threads::loop).start();
        new MyThread().start();
        new MyThread().start();
        new MyThread().start();
        loop();

        try {
            t1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
英文:

A Thread is created only when the start() method is called.
You can check it out on the JDK doc here:
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Thread.html

The following code shows you how this works

import java.util.Random;
public class Threads {
private static final Random rnd = new Random();
private static int rand(int a, int b) {
synchronized (rnd) {
return rnd.nextInt(b - a) + a;
}
}
private static void loop() {
while (true) {
synchronized (rnd) {
try {
Thread.sleep(rand(200, 1000));
} catch (InterruptedException e) {}
System.out.println(&quot;Hi, i&#39;m &quot;);
System.out.println(&quot;thread #&quot; + Thread.currentThread().getId());
}
}
}
public static class MyThread extends Thread {
@Override
public void run() {
loop();
}
}
public static void main(String[] args) {
Thread t1 = new MyThread();
t1.start();
new Thread(Threads::loop).start();
new MyThread().start();
new MyThread().start();
new MyThread().start();
loop();
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

huangapple
  • 本文由 发表于 2023年7月20日 20:31:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/76729903.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定