关于Java线程中this.getName()和Thread.currentThread().getName()区别的深入分析
本文为本人原创,转载请标明出处。
先看测试代码和运行结果:
问题的焦点在于运行结果标注的三个位置,为什么会是这三种结果?
分析:
逐个来看,先看运行结果1为什么是Thread-0。首先,子类的构造函数如果没有显式地指定调用父类的哪个构造方法,则在子类构造方法的第一行默认调用父类无参构造器,即隐式地调用super();其次,实例化子类要先实例化父类。本例中MyThread7是Thread的子类,而子类中没有重写父类的getName()方法(事实上,Thread类的getName()方法被final修饰不能被重写),所以在代码第13行的this.getName()调用的是父类的方法。前面已提到在实例化子类之前要先实例化父类,也就是说对于本例会先调用父类的默认构造器:
而nextThreadNum()的源码为:
可见线程默认的名字是这么来的。然后我们看一下Thread默认构造器里面的init()方法:
在这里可以看到在init()方法里面给成员变量name进行赋值:
至此,父类Thread对象初始化完毕(父类的name属性已经为Thread-0),开始进行子类初始化工作,即运行MyThread7构造器后面的代码。所以,到了代码的第13行打印出的名字是父类刚刚初始化的名字Thread-0。
再来看运行结果的第2处和第3处,这里有点不好理解——为什么打印的名字不一样?而对于这个问题的关键在于对Thread.currentThread()和代理模式的理解。先来看Thread.currentThread(),这个很简单,代表的是运行本行代码的线程或者说本行代码是运行在哪个线程里面。第二个就是代理模式,为什么是代理模式呢?来看一下Thread类的run()方法:
target是什么?接着看:
在本示例代码中target也就是第35行作为参数传递给Thread构造器的myThread7对象。当运行37行的thread.start()之后(获取CPU时间片这些细节忽略),调用的是thread对象的run()方法,而在run()方法内部target不为空,所以调用的是myThread7对象的run()方法,而MyThread7已经重写了run()方法。那么关键的地方到了:第19行的Thread.currentThread()到底是谁呢?这里再强调一下Thread.currentThread()的理解:调用本方法的线程是谁,或者说本方法是在哪个线程里面被调用的。不要在MyThread7类重写的run()方法里面找答案,否则就陷入了“不识庐山真面目,只缘身在此山中”的错误,而要到更高的层面去看,要到这里来找答案:
所以清楚了,调用myThread7线程run()方法的是thread线程,也就是说第19行的Thread.currentThread()指的是thread线程,而在36行设置了线程的名字为aaa,所以打印结果的第2处为aaa。接着来看第3处,这里就是对this指针的理解了,毋庸置疑,this指针指代的是调用本方法的对象是谁,前面已经分析过最终调用的是myThread7对象的run()方法,所以这里的this指代的是myThread7。前面已经分析过myThread7没有也不能复写父类Thread的getName()方法,所以this.getName()的值就是上面最开始分析的父类默认的线程名称Thread-0。再来看一下Thread类的run()方法:
我们可以打开实例代码的第20和22行代码来进行验证: