线程类Thread详解和线程的几种状态,守护线程

时间:2022-05-26 23:29:01

Thread类也是实现了Runnable接口的,它的run方法就是因此而来的

class Thread implements Runnable {}

Thread几个比较重要的构造方法:
Thread() //创建一个线程
Thread(Runnable target) //将一个实现了Runnable接口的类的对象传进来
Thread(String name) // 为线程传进名字

为什么必须要通过start方法才能启动一个线程呢?让我们看一下start的代码:
首先需要注意的一点main函数的线程和系统group线程不会被start启动
第二:线程如果已经执行完一次了,是不允许重新启动的
第三:start方法中需要为线程分配一定的必须的系统资源,比如为线程分配一个单独的栈空间,程序计数器,寄存器等这个线程的栈空间是线程私有的,里存放的是局部变量,对象的引用和方法的调用信息。 这也是为什么必须通过start来启动线程的原因。

    public synchronized void start() {
// threadStatus 表示这个线程有没有被执行过,如果已经执行完一次了,是不允许重新启动的
if (threadStatus != 0)
throw new IllegalThreadStateException();
// 有一个线程组维护所有启动的线程
group.add(this);
boolean started = false;
// 可以从下面看到start0()是一个本地方法,这也是为什么必须通过start来启动线程的原因,因为start方法里需要为线程的启动分配一定的资源。
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 */

}
}
}
//native 修饰的都是本地的方法,对我们是不可见的,由C或者C++实现
private native void start0();

下面是Thread类中的run方法,我们使用无参的构造函数的时候,target==null,使用有参的构造函数的时候调用的是传进来的实现了Runnable接口的类的对象的run方法,这就是两种线程实现方式的原因。

    public void run() {
if (target != null) {
target.run();
}
}

2) 当生成一个线程对象时,如果没有为其设定名字,那么线程对象的名字将使用如下形式:Thread-number,该 number 将是自动增加的,并被所有的 Thread 对象所共享(因为它是 static的成员变量) 。
3) 当使用第一种方式来生成线程对象时,我们需要重写 run方法,因为Thread类的run方法此时什么事情也不做。
4) 当使用第二种方式来生成线程对象时,我们需要实现 Runnable接口的 run方法,然后使用new Thread(new MyThread())(假如MyThread 已经实现了 Runnable接口)来生成线程对象,这时的线程对象的 run方法就会调用MyThread类的run方法,这样我们自己编写的 run方法就执行了。

停止线程绝对不能使用stop方法

线程的生命周期的几个状态
1. 创建状态(New): 当使用new关键字创建了一个线程,就处于创建状态
2. 可运行状态(Runnable):调用start方法,为线程分配一定的资源,但是线程还没有运行,但是处于可以运行的状态,还没有获取到CPU资源,run方法还没有被执行
3. 运行状态(Running): 运行状态,run方法在调用 ,注意的是调用了yield()方法,会自动让出对CPU的占用,转到 Runnable 状态。
4. 阻塞状态(Blocked): 进入阻塞的几种原因:
(1)调用了Thread的sleep(ms)方法变阻塞,以毫秒为单位;对应的时间过去了就会自动转化为Runnable状态
(2)调用的对象的wait方法变阻塞; 对应的必须等到对象调用了notify或者notifyAll方法,才能转化为Ruunble状态
(3)线程的输入输出I/O阻塞变阻塞;对应的必须等到I/O完毕,不可中断,才能转化到Runnable状态
5. 消亡状态 (Dead) : 线程死亡,不可能再用start启动
一个图完美的展现了这个过程:

注意的是: 一般线程在Runnable, Blocked, Running三个状态进行转换, Runnable和Running是双向的,它们的状态可以相互转化,其他都是单向的,状态 Blocked 只能转化成 Runnable状态,不可以直接到达Running状态

线程类Thread详解和线程的几种状态,守护线程

线程的优先级: 线程能不能被执行,优先级只是一方面,另一方面很大程度上是由操作系统底层决定的。千万不能完全依赖于优先级,因为很可能优先级低的永远得不到执行,发生饿死问题,所以优先级一般都是会动态调整的,随着时间的延长,优先级应该逐渐提高。
设置优先级是为了在多线程环境下便于系统对线程的调度,优先级高的线程将优先执行。
一个线程的优先级设置遵从如下的规则:
线程创建时,子类继承父类的优先级;
线程创建之后可以通过setPriority()方法改变优先级。
线程的优先级是1-10之间的正整数。默认的优先级NORM_PRIORITY是5

守护线程: Daemon Thread
java中的线程有两类,守护线程和用户线程
守护线程指在程序运行在后台的一种特殊线程,为其他的线程服务,比如垃圾回收线程就是一个很称职的守护线程。它并不属于程序中不可或缺的部分,但是确实是很有用的, 如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了,但是用户进程只要还存在一个,虚拟机就不会退出
守护线程有几个特点:
1、 thread.setDaemon(true)必须在thread.start()之前设置,你不能把一个正在运行的线程转化为守护线程
2、 在守护线程内的产生的线程也是守护线程
3、 永远不要试图利用守护线程去访问文件、数据库等固有资源,因为守护线程可以被任何用户线程所中断,线程的优先级最低