Jstack命令实战-线程方法使用
前面的文章,我们讲解了线程的6大状态及状态之间是如何切换的,这里面就涉及到了sleep,join等方法,到底这些方法该如何使用,我们为什么讲解线程方法的使用,因为我们要知道每个状态是如何产生的, 这样我们分析 jstack日志的时候,才能快速的定位问题
今天我们来讲解一下,线程方法的使用
1.线程方法
线程基本方法有很多,我们经常使用的大致分为以下几种
- 1、public void start() 线程启动方法,线程状态从New变为Ready状态
- 2、public void run() 线程的run方法是独立方法,如果独立执行,相当于main函数,方法执行
- 3、public final void setName(String name) 设置参数,修改线程名称
- 4、public final void setPriority(int piority) 设置线程的优先级,优先级1-10,1最低,5普通,10最高,系统根据优先级决定使用哪个线程
- 5、public final void setDaemon(boolean on) 将该线程标记为守护线程或用户线程,在主线程关闭后无需手动关闭守护线程
- 6、public final void join(long millisec) 等待该线程终止的时间,时长为 millis 毫秒,谁调用join方法,谁就强占cpu资源,直至执行结束
- 7、public void interrupt() 中断线程
- 8、public final boolean isAlive() 判断线程是否处于活动状态
- 9、public static void static yield() 放弃CPU资源,暂停并执行其他线程。
- 10、public static void sleep(long millisec) 在指定的毫秒数内让当前正在执行的线程休眠指定时
- 11、public static Thread currentThread() 返回对当前正在执行的线程对象的引用。
下面我们挑选几个常用的,或者比较容易混淆的,单独讲解
2.start 和 run的区别
Start()方法是线程启动方法,从New新建到Ready状态的转换
- new 一个Thread,线程进入了新建New状态
- 调用start() 方法,会启动一个线程并使线程进入了就绪Ready状态
- 当分配到 CPU的时间片 后就可以开始运行了Runnable
- start() 会执行线程的相应准备工作,然后
自动执行 run()方法的内容,这是多线程工作
Run()方法是Thread的普通方法
- 如果直接调用Run()方法,就像你写的test()方法一样,不会新开线程去工作
- 在主线程中去执行,它会把run 方法当成一个main 线程下的普通方法去执行
- 并不会新开线程,在新的线程中执行它,这并不是多线程工作
下面我们看下 start()方法的内部实现, start中有一个 start0()方法,该方法是native方法
private native void start0();
该方法并没有实现,是掉的底层C++的方法,native就是说明这个方法无法使用java平台来实现,是包装的底层本地方法
public synchronized void start() {
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
3.join 方法,到底是谁暂停,等待谁执行
3.1 无join方法,主线程先结束
join方法也是比较容易混淆的方法之一,为什么会有join方法?
- join方法是为了防止 某个线程执行完结束的
- 有些场景我们是需要 线程1 等待一会,等待线程2 线程结束,线程1再执行的
- 这时候哦我们就需要join方法
那么到底是 谁join,谁等待谁完成,下面我们来验证一下,先说下结论, 谁join,等待谁,比如thread1.join,那么就是要等待 thread1执行完毕,才能执行下一步
我们看下 测试方法
- Thread1 处理时间很长
- //thread1.join(); 注释掉join,此时刻没有join方法
- 主线程很快就执行完了,打印 主线程结束的日志
- 子线程还在不停的慢慢执行
- 如果主线程还有其他操作,依赖子线程的执行结果,这明显是不行的
- 这时候就需要 子线程thread1.join 等待thread1执行完,再执行其他
代码如下:
package com.jzj.jvmtest.mythread;
public class JoinTest {
public static void main(String[] args) throws InterruptedException {
//线程1 处理时间很长
Thread thread1 = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);//子线程处理中
System.out.println("子线程-线程1 执行中");
}
System.out.println("子线程处理完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
//如果不适用 join方法,可能主线程很快就结束了,打印了日志
// thread1.join();
//使用了join方法,1 就是 thread1.join(), 谁掉join,等待谁,等待子线程1执行完 主线程再处理
System.out.println("主线程结束");
}
}
执行结果如下,先主线程结束,然后慢慢的子线程结束
3.2 执行join,thread1.join,等待thread1执行完毕
现在,我们修改代码,采用thread1.join方法,等待thread1执行完,再处理主线程场景
thread1.start();
//如果不适用 join方法,可能主线程很快就结束了,打印了日志
thread1.join();
//使用了join方法,1 就是 thread1.join(), 谁掉join,等待谁,等待子线程1执行完 主线程再处理
System.out.println("主线程结束");
执行结果如下
子线程-线程1 执行中
子线程-线程1 执行中
子线程-线程1 执行中
子线程-线程1 执行中
子线程-线程1 执行中
子线程-线程1 执行中
子线程-线程1 执行中
子线程-线程1 执行中
子线程-线程1 执行中
子线程-线程1 执行中
子线程处理完成
主线程结束
先执行子线程,主线程不会结束,一直等到 join,子线程执行完毕,主线程才执行,打印主线程结束
3.3 执行join(5000) 最多等待5000ms
我们看到 子线程执行了10s, 如果 join传时间参数呢?
- join(5000) 5000ms 表示最多等待 5s, 之后再处理
- join的时间就是sleep的时间
- join后,原线程执行的就是wait,表示等待t1执行完后,才能继续执行
我们看到子线程执行了10s, 我们传参数join(5000), 应该是 5s后,主线程开始执行,下面我们验证下 修改代码
thread1.start();
//如果不适用 join方法,可能主线程很快就结束了,打印了日志
thread1.join(5000);
//使用了join方法,1 就是 thread1.join(), 谁掉join,等待谁,等待子线程1执行完 主线程再处理
System.out.println("主线程结束");
执行结果如下,最多等待 5s后,主线程就开始处理了
4.yield 和sleep 方法的区别
yield 和 sleep都会使当前线程暂停,那么他们之间的区别是什么?
yield :暂停当前正在执行的线程对象,并执行其他线程,它还是一定会执行其他线程还是说可能会执行其他线程,这就的看yield到底干了什么事情
- yield 暂停当前线程运行状态,变为Ready就绪状态
- 适用于相同优先级的线程间,适当的轮转
- 释放CPU资源,以便让其他优先级相同的线程获得机会
- 既然你和其他线程优先级相同,那么无法保证一定让步,因为你让步后,释放了cpu资源
- 你和其他线程都是相同优先级,有可能你下次仍然被选中,再次恢复运行中Runable状态
- 所以yield当前线程让步了,但是可能没让步成功,下次还是选中了它
我们再来看看sleep干了什么事, sleep 解释如下
- sleep()方法暂停当前线程后,会给其他线程执行机会,不区分其他线程的优先级
- sleep()方法会将线程转入阻塞Blocked状态,而不是 就绪Ready状态
- sleep() 存在异常InterruptedException,,所以调用sleep()方法时要么捕捉该异常,要么显式声明抛出该异常
- 如果调用了sleep方法之后,没有其他等待执行的线程,这个时候当前线程不会马上恢复执行
所以,他们俩还是有区别的,区别如下
- sleep没优先级 ,yield有优先级,给相同或者高优先就让步
- sleep进入阻塞blocked状态,yield进入就绪状态
- sleep不会被cpu立马选中,但是yield可以下一次立马被选中执行
- sleep会有中断异常InterruptedException声明,yield没有
至此,我们讲解了线程的一些基本方法,并且对方法做了对比,对比了start和run方法的区别,yield和sleep方法的区别及join方法的使用等,下一篇,我们介绍下jstack 如何定位并解决blocked及wait等待状态的问题