Thread构造方法与启动

线程类一共有八种构造方法


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

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

    public Thread(ThreadGroup group, Runnable target) {
        init(group, target, "Thread-" + nextThreadNum(), 0);
    }

    public Thread(String name) {
        init(null, null, name, 0);
    }

    public Thread(ThreadGroup group, String name) {
        init(group, null, name, 0);
    }

    public Thread(Runnable target, String name) {
        init(null, target, name, 0);
    }

    public Thread(ThreadGroup group, Runnable target, String name) {
        init(group, target, name, 0);
    }

    /**
     * 分配一个新的Thread对象,并传入一个target作为其运行对象,将指定线程名name,
     * 以及所属线程组group ,并具有指定的堆栈大小stackSize 。
     * 堆栈大小是虚拟机为该线程的堆栈分配的大致的地址空间字节数。
     * stackSize参数的(如果有的话)与平台有关。
     *线程栈的大小,这个值一般是CPU页面的整数倍。如x86的页面大小是4KB。在x86平台下,默认的线程栈大小是 12KB。
     * 在某些平台上,指定了一个较高的值stackSize参数可以允许抛出一个前一个线程来实现更大的递归深
     * 度StackOverflowError 。 类似地,指定较低的值可能允许更多数量的线程同时存在,而不会抛出
     * OutOfMemoryError (或其他内部错误)。 所述stackSize参数的值和最大递归深度和并发水平之间
     * 的关系的细节是依赖于平台的。 在某些平台上,该值stackSize参数可能没有任何效果。
     *
     * stackSize参数只是被当作一种建议大小传入虚拟机。 如果平台的指定值不合理地低,虚拟机可能会改为
     * 使用一些平台特定的最小值; 如果指定的值不合理地高,虚拟机可能会使用一些平台特定的最大值。
     * 同样,虚拟机可以自由地按照合适的方式向上或向下舍入指定的值(或完全忽略它)。
     *
     * 由于此构造函数的行为有依赖于平台的性质,因此在使用时应特别小心。 执行给定计算所需的线程栈大小
     * 可能会因JRE实现而异。 鉴于这种变化,可能需要仔细调整堆栈大小参数,并且可能需要对要运行应用程序
     * 的每个JRE实现重复调整。
     */
    public Thread(ThreadGroup group, Runnable target, String name,
                  long stackSize) {
        init(group, target, name, stackSize);
    }

当采用 new方式创建一个线程对象时,线程构造器会调用一个**init()**方法来进行初始化。

	/**
     * Initializes a Thread with the current AccessControlContext.
     * 使用当前的AccessControlContext初始化线程,八种构造方法都会先进入到这里
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }

    /**
     * 初始化一个线程
     * @param g 线程组
     * @param target 调用run()方法的对象
     * @param name 线程名
     * @param stackSize 新线程的所需堆栈大小,0表示该参数将被忽略。
     * @param acc 要继承的AccessControlContext;如果为null,则为                                	AccessController.getContext()
     * @param inheritThreadLocals 如果为true,则从构造线程继承可继承线程局部变量的初始值
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
        this.name = name;//给线程名赋值
        Thread parent = currentThread();//获取当前线程
        SecurityManager security = System.getSecurityManager();//获取系统安全管理者
        if (g == null) {
            /* 如果有安全管理者, 先尝试获取安全管理者的线程组*/
            if (security != null) {
                g = security.getThreadGroup();
            }
            /* 如果此时线程组g还是null会采用当前线程的线程组。*/
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }
        //对线程组g进行检查
	   //主要检查:1.是否线程组为null 2.是否根线程组,如果是根线程组则要检查其是否有修改权限
        g.checkAccess();
//权限检查:子类不能覆盖对安全性敏感的方法,否则将检查“enableContextClassLoaderOverride”运行时权限
        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;//为Runnable实例赋值
        setPriority(priority);//设置线程优先级
        //设置inheritableThreadLocals属性
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                    ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        this.stackSize = stackSize;//设置栈大小
        tid = nextThreadID();//设置线程ID(此方法已加锁)
    }

启动线程

    /**
     *调用此方法,这个线程开始执行,jvm虚拟机会调用run()方法
     * 一个线程被启动多次是不合法的
     * 特别是,线程一旦完成执行就可能不会重新启动。
     */
    public synchronized void start() {
        /**
         *VM创建/设置的主方法线程或“系统”组线程不会调用此方法。将来可能会向该方法添加任何添加到此方法的新功能。
         * 零状态值对应于状态“ NEW”
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* 通知组该线程将要启动,以便可以将其添加到组的线程列表中,并且可以减少该组的未启动计数。 */
        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
                  不做什么。如果start0抛出了Throwable,则它将被向上传递到调用堆栈*/
            }
        }
    }

    private native void start0();//本地方法,真正启动线程的方法

总结:让线程对象调用start()方法和run()方法的区别。

线程是一条按照顺序执行的任务流程。无论是单线程还是多线程,每条线程中的任务都必须按顺序完成。

如果现在我们希望有些任务能够同时执行,那必须把它们分别放在不同的线程中。

想要实现多线程同时执行的效果,必须要有一个行为能够开启新的线程。

run()方法本身没有特殊功能。如果直接调用run()方法,并没有开启新的线程。就和我们之前调用一个add()、setName()、getAge()、sayHello()等等方法是同样的效果。只是在main这一条线程中,进入到了另外一个方法的执行。那么就必须要等这个方法中所有的代码全部执行完毕,才会返回到调用处继续执行后边的代码。

如果调用start()方法,会间接调用start()中一个名为strat0()的native方法,在start0()的底层实现过程中,才会真正开启另外一条线程。这条线程会调用我们线程对象的run()方法。start()方法可以理解为是一种短暂性、瞬时性的方法。只需要将线程开启,把它丢到一旁去自己运行,而start()就算是执行结束了,并不需要等这条线程结束。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值