java线程基础巩固---构造Thread对象你也许不知道的几件事

关于Thread的构造在JDK文档中如下:

之后会把上面所有的构造都会学习到,这次主要是去研究一下图上标红的默认构造,当然大家肯定对于它都有些不屑,这有啥可学的,不new一个然后start线程不就启动了,属于线程最最基础的东东,然后它的背后并非我们都知道,所以下面开始学习。

Thread常规知识:

先来看一下默认构造的源码:

先了解一下默认线程名的起名规则,看下nextThreadNum():

原来是有一个静态计数的变量,那也就是说默认new出来的线程是以"Thread-0、Thead-1"这样的规则来命名的,用代码来验证下:

编译运行:

果真如预期,但是目前线程中并未做任何事情,而我们知道可以给线程构造中传一个Runnable接口:

那在默认构造Thread的时候有没有一个空的Runnable去执行呢?看源码【贴出关键代码】:

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

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null);
    }

从代码中可以看到并未自动创建一个Runnable,而是直接传的null,那最终在init()中是如何搞的呢?

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

        this.name = name.toCharArray();

        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

只看核心代码,可以看到将Thread的成员变量target赋值为null了,

那此时我们执行创建的线程会调用start()方法,那接着看下它:

public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();

最终转到C层去执行线程的启动了,而底层的代码暂且不深纠了,这里要知道C层会去调用Thread的run()方法,所以转到run方法去看一眼:

但是!!!可以重写它的run方法来执行我们的动作,如下:

编译运行:

那接着修改代码:

接着在创建runnable的时候也手动给它取个名字,如下:

这样关于Thread的构造就学习了四种了,当然也是最经常使用的,比较简单:

ThreadGroup的概念及守护线程初探:

关于这个内容可能就不是太清楚啦,可以看一下剩下线程构造函数中:

全是跟ThreadGroup相关,貌似实际工作中完全木有接触过它,所以说首先得了解它,从哪了解呢,直接从源码:

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

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

        this.name = name.toCharArray();

        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

从源码中可以看到,默认无参的构造函数并未指定ThreadGroup,当在执行init()方法时发现没有ThreadGroup,则会获得父线程的ThreadGroup,而父线程可以通过currentThread()方法获取,而我们在之前【http://www.cnblogs.com/webor2006/p/7682063.html】有介绍过,对于创建一个线程实际是有两个线程,其中有一个就是main线程,也就是用来启动我们自己创建的线程的,那是不是我们线程的parent就是main呢?下面来做实验论证:

那如果此时新建一个线程,然后再看一下它的ThreadGroup,如下:

那说明创建线程时未指定ThreadGroup,那会以父线程的ThreadGroup作为该线程的ThreadGroup,刚好跟源码逻辑吻和,那是不是说子线程将和父线程在同一个ThreadGroup中?也就是说目前ThreadGroup中应该包含了2个线程,那我们打印看一下:

编译运行:

这是在MAC上的表现,但是!!!如果在windows平台其输出并非如我们所预期的,下面在windows下运行看一下结果:

总结:

  • 创建线程对象Thread,默认有一个线程名,以Thread-开头,从0开始计数:Thread-0、Thread-1、Thread-2等。
  • 如果在构造Thread()的时候没有传递Runnable或者没有复写Thread的run方法,该Thread将不会调用任何东西;如果传递了Runnable接口的实例,或者复写的Thread的run方法,则会执行该方法的逻辑单元(逻辑代码)。
  • 如果构造线程对象时未传入ThreadGroup,Thread会默认获取父线程的TreadGroup作为该线程的ThreadGroup,此时子线程将和父线程在同一个ThreadGroup中。
原文地址:https://www.cnblogs.com/webor2006/p/7760422.html