多线程--2
run()和start() 线程的运行状态 获取线程对象以及名称
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
1. run()和start()关系
1). Thread类的run()方法
(1).Thread子类一定要覆盖父类run()方法的原因
[1]. Thread类的run()的作用:用于存储非主线程线程代码
[2]. 子类重写run方法的目的:将需要自定义线程运行代码存放在run()方法中,等待自定义启动之后,运行run()中的内容。
(2). 主线程直接调用线程子类的run()方法的结果
如果主线程仅仅调用子类的run()方法的结果就是主线程直接执行线程子类的run()方法的代码,没有启动另一个进程/程序控制单元。整个程序/进程仍然是单线程执行。
2). Thread类的start()方法
(1). 主线程中要调用start()方法而不是调用run()方法的原因
[1]. Thread类的start()的作用:用于开启线程并执行自定义在run()中的代码
[2]. 主线程必须调用线程实例的start()的原因:调用run()之后,成个程序仍然是单线程、调用start()方法之后,start中的本地native方法start0()方法会调用操作系统的底层的资源创建相关的线程。这个时候,这个的进程才是多个线程来执行。
(2). start()的相关知识点
[1]. 主线程调用某个线程对象的start()之后,这个线程一定会马上执行么?
不一定!!!!!!!!
因为CPU在某一个时刻仅仅能执行一个进程中的一个线程,进程中其余的线程都处于临时阻塞状态,并非处于运行状态,都在等待获取CPU的执行权
2. 线程运行状态转换
1). 线程运行状态转换图
2). 各种状态的转换关系
(1). 调用start()方法
调用start()方法会使得线程从新建状态发生状态跳转。最有可能跳转到临时阻塞状态,同时也有很小的可能使得该线程立刻被执行,处于运行状态。
因为运行在一个进程中的线程有很多,但是一个时刻只能有一个线程被CPU执行。所以,只有很小的可能这个线程会抢到CPU的执行权。很大的可能性这个线程处于等待获取CPU执行权的状态,这就是该线程处于临时阻塞状态。
(2). 调用sleep(时间)方法或者wait()方法
线程如果碰到sleep(时间)方法或者wait()方法,就会立刻变成冻结状态。放弃CPU的执行权。
(3). sleep()运行时间到或者调用notify()方法
当sleep()运行时间到或者调用了notify()方法,处于冻结状态的线程会激活。很有可能跳转到临时阻塞状态。有很小的可能回到运行状态。
(4). stop()或者run()方法运行完成
当调用stop()方法或者run()方法被线程运行完成,线程就从运行状态走向了消亡状态。
(5). 临时阻塞状态和运行状态
这两个状态之间的转换完全是看相应的线程有没有CPU执行权。如果有,就执行。没有,就等待。
【总结】临时阻塞状态是非常重要的!!!!!除了线程的被创建的状态和消亡状态之外,运行状态、冻结状态都是临时阻塞状态有紧密联系的。
【注意】被创建的状态和消亡状态的不可逆转性
{1}.不能从其他状态再回到被创建的状态
{2}. 不能从消亡状态再回到其他状态
3). 具有CPU执行资格和具有CPU执行权
递进关系:只有具备CPU执行资格的线程才能谈是否具有CPU执行权
| 是否具有CPU执行资格 | 是否具有CPU执行权 |
运行状态【双有】 | 有 | 有 |
冻结状态【双无】 | 无 | ---- |
临时阻塞状态【一有一无】 | 有 | 无 |
【注意】有的地方又把冻结状态分成睡眠状态和等待状态
3. 获取线程对象以及线程对象的名称
(1). Thread类的源码开始
public
class Thread implementsRunnable {
/* Make sure registerNatives isthe first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
private char name[];
…
}
(1). 线程对象的name属性
[1].每一个线程都有自己的名字。所以在Thread类中,有一个非静态的成员变量:字符数组name[];。这样,就有对应的setter和getter来访问这个表示线程名称的属性。
[2]. 默认的线程的名称:“Thread-编号”(编号从0开始)
(2). 获取线程的名称 ---name的getter
[1]. 获取线程对象的名字就是用Thread类的getName()方法即可
[2]. Thread类的getName()的源码:
public final String getName(){
return String.valueOf(name);
}
(3). 设置Thread类对象的名称
[1]. 通过构造函数的初始化方式为线程对象设置对应的线程名称
{1}. Thread类的空参构造函数源码
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
//黄色背景是默认初始化名称
}
{2}. Thread类的String列表的构造函数
public Thread(String name) {
init(null,null, name, 0); //黄色背景是用户传如的线程的名字
}
[2]. 通过name属性的setter:setName()方法为线程改变线程名称
public final void setName(String name) {
checkAccess();
this.name = name.toCharArray();
}
【总结】改变一个对象的某个私有属性一般有两种方式:
初始化的时候改变 ----构造函数
任意时刻改变 ----属性的getter
(4). 获取当前被CPU执行的线程对象
[1]. 通过Thread类的静态方法 currentThread()方法来获取。
[2]. 源码:public static native ThreadcurrentThread();
这个方法是本地化方法。
[3]. currentThread()方法的用处
{1}. run()方法中Thread.currentThread()返回值就是this变量
{2}. run()方法以外,Thread.currentThread()获取当前被执行的线程
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------