Thread 类中的几个细节(一)

版权声明: https://blog.csdn.net/Dongguabai/article/details/84761227

相关博客:

Thread 类中的几个细节(二)

构造 Thread 类

先看这么一段代码:

package com.example.threaddesign;

/**
 * @author Dongguabai
 * @date 2018/12/2 20:58
 */
public class ThreadTest {

    public static void main(String[] args) {
        Thread t = new Thread();
    }

}

这里定义了一个 Thread 实例 t,目前 t 是处于 NEW 状态(并发编程学习之线程的生命周期(状态))。当前程序中只有一个线程,就是 main 线程。main 线程是 JVM 去创建的,而 Thread 只有在 start() 的时候才会产生。

Thread 的构造方法如下:

public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

看看这个 init() 方法:

 /**
     * Initializes a Thread with the current AccessControlContext.
     * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext)
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null);
    }

这个方法有 4 个参数,第一个参数是选择 ThreadGroup;第二个参数可以看成是策略接口;第三个参数是线程的名称;最后一个参数是栈的大小。

而这个 init() 方法又调用了另外一个重载的 init() 方法:

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
...

最后一个参数 AccessControlContext 是与 JVM 权限有关,这里不用管。

线程名称

从这个方法的第一行代码可以看出,名称是不能为 null 的,那么 Thread 的命名规则是怎么样的呢,这里先测试一下:

运行结果:

那再创建一个 Thread,命名是怎样的呢?

运行结果:

回到 Thread 的无参构造函数:

查看 nextThreadNum() 方法:

 /* For autonumbering anonymous threads. */
    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }

这个方法就很好理解了。这里的加锁还是 static 级别的。

也就是说创建一个 Thread,默认线程名是 Thread-0。我们也可以传入 Thread 的名称(具体可参看:创建线程时候声明线程名称

target 参数

再看 Runnable target 参数。

在 init() 方法中可以看到:

...
this.target = target;
...

也就是说这里没有默认值了,传过来是 null 就是 null。start() 方法最终会执行它的 run() 方法:

 /**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

如果构造传入 Runnable,就执行 Runnable 的 run() 方法,否则就需要重写 Thread 的 run() 方法,如果没有重写就啥也不做。

ThreadGroup 参数

在 init() 方法中:

 /**
     * Initializes a Thread with the current AccessControlContext.
     * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext)
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null);
    }

接着看重载的 init() 方法:

如果 g 为 null 就会走上面那一段逻辑,那个 security 先不管。而这个 parent 就是当前线程。也就是说如果构造线程对象时未传入 ThreadGroup,Thread 会默认获取父线程的 ThreadGroup 作为该线程的 ThreadGroup,此时子线程和父线程在同一个 ThreadGroup 中。

 public static void main(String[] args) {
        Thread t = new Thread();
        t.start();
        System.out.println(t.getThreadGroup().getName());    //main
        System.out.println(Thread.currentThread().getName());   //main
        System.out.println(Thread.currentThread().getThreadGroup().getName());  //main
    }

ThreadGroup 中还有一些其它的 API,这里可以看看:

package com.example.threaddesign;

import java.util.Arrays;

/**
 * @author Dongguabai
 * @date 2018/12/2 20:58
 */
public class ThreadTest {

    public static void main(String[] args) {
        Thread t = new Thread(()->{
            //保证 t 存活
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t.start();
        //获取当前线程线程组
        ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
        //获取当前线程组中的活跃的线程数量
        int activeCount = threadGroup.activeCount();
        System.out.println(activeCount);
        
        Thread[] arr = new Thread[activeCount];
        //将当前线程组中的线程赋值至数组中
        threadGroup.enumerate(arr);
        Arrays.asList(arr).forEach(System.out::println);
        System.in.read();
    }

}

猜想的话当前活跃线程应该有两个,一个是 main 线程,一个是 t 线程。

输出结果:

发现还有一个 Monitor 线程,用来监听一些事件。

也能够看出 Monitor 线程是一个守护线程:

C:\Users\Dongguabai>jps
4496
15284 Launcher
2964 Launcher
4420 ThreadTest
9736 Jps

C:\Users\Dongguabai>jps -l
4496
15284 org.jetbrains.jps.cmdline.Launcher
2116 sun.tools.jps.Jps
2964 org.jetbrains.jps.cmdline.Launcher
4420 com.example.threaddesign.ThreadTest

C:\Users\Dongguabai>jstack --help
Usage:
    jstack [-l] <pid>
        (to connect to running process)
    jstack -F [-m] [-l] <pid>
        (to connect to a hung process)
    jstack [-m] [-l] <executable> <core>
        (to connect to a core file)
    jstack [-m] [-l] [server_id@]<remote server IP or hostname>
        (to connect to a remote debug server)

Options:
    -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
    -m  to print both java and native frames (mixed mode)
    -l  long listing. Prints additional information about locks
    -h or -help to print this help message

C:\Users\Dongguabai>jstack -l 4420
2018-12-03 18:45:40
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.65-b01 mixed mode):

"Service Thread" #10 daemon prio=9 os_prio=0 tid=0x0000000019ab1800 nid=0x11a8 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"C1 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x00000000199dc000 nid=0x418 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x00000000199db000 nid=0x25c8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x00000000199da000 nid=0x26ac waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x0000000019976000 nid=0x417c runnable [0x0000000019efe000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
        at java.net.SocketInputStream.read(SocketInputStream.java:170)
        at java.net.SocketInputStream.read(SocketInputStream.java:141)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
        - locked <0x00000000d6259a10> (a java.io.InputStreamReader)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.BufferedReader.fill(BufferedReader.java:161)
        at java.io.BufferedReader.readLine(BufferedReader.java:324)
        - locked <0x00000000d6259a10> (a java.io.InputStreamReader)
        at java.io.BufferedReader.readLine(BufferedReader.java:389)
        at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)

   Locked ownable synchronizers:
        - None

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x00000000184ad000 nid=0x15fc waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x0000000019843800 nid=0x2e4 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x0000000003969000 nid=0x40d4 in Object.wait() [0x00000000197ff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000d5f870b8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
        - locked <0x00000000d5f870b8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

   Locked ownable synchronizers:
        - None

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000018459000 nid=0x26d4 in Object.wait() [0x00000000196ff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000d5f86af8> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:157)
        - locked <0x00000000d5f86af8> (a java.lang.ref.Reference$Lock)

   Locked ownable synchronizers:
        - None

"main" #1 prio=5 os_prio=0 tid=0x0000000003874000 nid=0x1124 runnable [0x000000000327f000]
   java.lang.Thread.State: RUNNABLE
        at java.io.FileInputStream.readBytes(Native Method)
        at java.io.FileInputStream.read(FileInputStream.java:255)
        at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
        at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
        - locked <0x00000000d5fdca10> (a java.io.BufferedInputStream)
        at com.example.threaddesign.ThreadTest.main(ThreadTest.java:32)

   Locked ownable synchronizers:
        - None

"VM Thread" os_prio=2 tid=0x0000000018458000 nid=0x3514 runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000003889800 nid=0x4220 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x000000000388b000 nid=0xac0 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000000000388c800 nid=0x2e10 runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000000000388e000 nid=0x1520 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x0000000019ae1000 nid=0x37b4 waiting on condition

JNI global references: 341

猜你喜欢

转载自blog.csdn.net/Dongguabai/article/details/84761227