Thread start/run方法分析

仔细琢磨 start 方法,我们可以得到很多结论

查看 Thread 中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 */
            }
        }
    }

1. 线程无法被启动两次,有一个线程状态值 threadStatus 记录了线程状态(猜测应该是在start0()方法中赋值),如果重复启动,会抛出 IllegalThreadStateException 

2. 我们无法得知线程启动过程中做了什么,因为主要逻辑在本地方法 start0 当中,但不难知道,真正启动一个操作系统内核级线程并执行run方法中的任务也是在 start0 方法中进行的java 线程是基于操作系统内核级线程进行映射的,所以最终需要借助于操作系统。

Java线程和操作系统线程的关系可参考https://blog.csdn.net/CringKong/article/details/79994511

3. 每个线程都有自己的私有堆栈,异常不能跨线程传播

举个栗子,a线程中启动b线程b.start(),注意b线程真正开始执行是在start0()方法中,所以前面抛出的IllegalThreadStateException 属于a线程中抛出的异常。假如在执行start0方法时抛出运行时异常(意味着b线程启动失败),不进行catch操作,不处理异常信息,会被沿着调用堆栈(a线程的堆栈)上抛。同时finally语句块中保证了在start0出现异常,b线程启动失败后做相应的逻辑处理。

假如start0方法正常执行完毕,b线程成功启动,那么a,b线程将会在各自的堆栈中并发执行,互不影响。b线程在执行任务(执行run方法内容)过程中,假如出现了异常,它会沿着b线程的调用堆栈传递上抛,直至被 try catch捕捉,即使没有捕捉,它也不会影响a线程的执行。

4. Thread是Java语言本身对线程的抽象,也就是说在Java中,线程只有一种形式,那就是Thread的实例形式存在

有一个经常被问的问题,创建线程有几种方式?我觉得这个问题其实不好,创建一个Thread的实例对象只有一个途径,那就是借助于new。而我们更多的是需要关注封装任务的方式,如何改变这个run方法为你需要的任务代码?

观察run方法的源码,可以发现我们其实只有两种方式,

1. 继承Thread,重新run方法

2. 通过构造方法,透传 Runnable 实例对象,对target进行赋值

单论创建线程,跟Runnable没必然关系,不用Runnable也能创建线程,任务封装才会涉及到Runnable。

3. 其实还有一种间接利用Runnable封装任务的方式,实现Callable接口,并且借助于FutureTask(关于这个,另做记录)

总结为

image_5c5bc7ee_2eb0

部分内容参考https://www.cnblogs.com/noteless/p/10354753.html强推

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值