目录
1、等待一个线程:join
1.1 join()
1.2 join(long millis)——"超时时间"
1.3 join(long millis,int nanos)
2、获取当前线程的引用:currentThread
3、休眠当前进程:sleep
3.1 实际休眠时间
3.2 sleep的特殊写法——sleep(0)
4、线程状态
4.1 NEW
4.2 TERMINATED
4.3 RUNNABLE
4.4 TIMED_WAITING
4.5 WAITING
1、等待一个线程:join
1.1 join()
在并发中,我们通常用这样一个需求,一个线程执行完后,另一个线程才能终止,也就是需要控制两个线程结束的先后顺序。
我们可以通过上篇博客所提到的sleep来设置线程的休眠时间,从而控制线程的结束的先后顺序,但是这样的做法是不科学的。比如,我们需要在t线程结束后,让main线程紧跟着结束,此时sleep就显得不靠谱了~~
我们就可以通过:
- 在main线程中调用t.join()方法,来让main线程等待 t 线程结束后,main再结束。
- 当代码执行到t.join时,main线程就会发生"阻塞等待",等待t线程结束后,join再继续执行。
join方法也会抛出InterruptedException异常。
注意:在哪个线程中调用join,就是哪个线程在"阻塞等待"。
1.2 join(long millis)——"超时时间"
其实上文的join是不科学的"等待",其实就是不见不散的“死等”,要是另t线程一直没有结束,main线程就会一直等待下去。
join还提供了另外一个版本,带参数的join(long millis),带有“超时时间”的等待,“超时时间”即最大的等待时间,当等待的时间超过设定的“超时时间”后,main就不会继续等待下去了,继续执行join下面的代码。
例如:在main线程中调用t.join(3000):
- 当main等待的时间超过3秒后,t线程还没有结束,main就不会继续等待,继续行join下面的代码。
- 当main等待的时间还没有超过3秒时,即在3秒之内t已经执行完了(t提前结束),此时main也不会再等待了,也会继续行join下面的代码。
带有超时时间的等待,才是更科学的等待(当电脑或者手机程序卡死的时候,就会弹出等待时间的窗口)。尤其是在和网络通信相关的领域都是需要设置"超时时间"。
public class Demo10 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
Thread.currentThread();
for(int i = 0; i < 3000; i++) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("t 线程终止");
});
t.start();
// main 等待 t
//t.join();
//main 最多等待 t 3秒
//t.join(3000);
t.join(3000,500);
System.out.println("main 终止");
}
}
1.3 join(long millis,int nanos)
join(long millis,int nanos),nanos即纳秒,即"超时时间"精确到纳秒级别,是更为精确的等待。对于我们开发来说,几乎不会使用。
1s = 1000ms(毫秒),1ms = 1000us(微秒),1us = 1000ns(纳秒)
在计算机中,很难做到ns级别的精确测量,即使线程的调度也是ms级别的开销。
但是也并非做不到,"实时操作系统"就可以做到更为精确的时间计算,我们平常接触到的Windows、Linux、Mac、Android系统都不是实时操作系统,这类操作系统常用于航天、军事、工业领域。实时操作系统,其实时性非常高,但是也是在削弱很多功能下达到的,俗话说得好,鱼和熊掌不可兼得~~
2、获取当前线程的引用:currentThread
这个方法我们已经很熟悉了,上篇博客已经为大家进行了讲解。
我们只需要记住一点:在哪个线程中调用,获取的就是哪个线程的引用(类似于this)。
3、休眠当前进程:sleep
3.1 实际休眠时间
这个方法我们也是很熟悉的了。
但是要额外注意一点:实际的休眠时间,往往是要大于我们所设置的休眠时间的。
使用sleep方法让线程休眠时,实际是让当前线程让出CPU资源,当休眠时间一到,只能说明当前线程是允许被操作系统调度到CPU上执行了,而并不是说明是立即被执行。
也就是说还会有一些线程被调度的时间的开销,一般是ms级别的开销。
3.2 sleep的特殊写法——sleep(0)
sleep(0)是使用sleep的一种特殊写法。意味着让当前线程立即放弃CPU资源,让给其他线程,再等待操作系统重新调度。
当一个模块CPU占有率比较高,影响到其他模块正常执行时,就可以通过这种方式来缓解资源紧张。
到目前为止,基于对Thread类的学习,我们已经掌握了:
- 创建线程
- 关键属性
- 终止线程
- 线程等待
- 获取线程引用
- 线程休眠
接下来,我们再谈线程状态~
4、线程状态
我们知道 进程 分为以下两种状态:
- 就绪
- 阻塞
但是这两种状态都是站在操作系统的视角所定义的,在Java线程中,Java也是对操作系统线程进行了封装,对于线程状态也是重新进行表示。
- NEW: 安排了工作, 还未开始行动
- TERMINATED: 工作完成了
- RUNNABLE: 可工作的。又可以分成正在工作中和即将开始工作
- TIMED_WAITING: 这几个都表示排队等着其他事情
- WAITING: 这几个都表示排队等着其他事情
- BLOCKED: 这几个都表示排队等着其他事情
4.1 NEW
NEW状态是指:仅仅new好了Thread对象,但是还没有创建线程(还没有start)。
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (true) {
System.out.println("hello t");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
// NEW 状态
System.out.println(t.getState());
}
4.2 TERMINATED
TERMINATED状态是指:线程已经结束,但是Thread对象依旧存活。即:线程执行完毕。
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
System.out.println("hello t");
});
t.start();
Thread.sleep(1000);
// 此时 t 线程已结束
// TERMINATED
System.out.println(t.getState());
}
4.3 RUNNABLE
RUNNABLE状态其实就是就绪状态,分为以下两种:
- 线程正在CPU上执行
- 线程随时可以去CPU上执行
RUNNABLE是处于NEW和TERMINATED之间的状态。
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (true) {
}
});
t.start();
// t 线程正在执行
// RUNNABLE
System.out.println(t.getState());
}
4.4 TIMED_WAITING
TIMED_WAITING状态是一种阻塞状态(不参与CPU调度,不继续执行了),但是是有指定时间的阻塞,阻塞的时间有上限。
当线程处于以下状态时,就为TIMED_WAITING状态。
- sleep指定时间内休眠时
- 处于使用带"超时时间"的阻塞等待时
线程sleep前为RUNNABLE状态,sleep时为TERMINATED状态,sleep后又回到RUNNABLE状态。
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (true) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
// TIMED_WAITING
Thread.sleep(1000);
System.out.println(t.getState());
}
4.5 WAITING
WAITING也是一种阻塞状态,只不过是死等,即没有"超时时间"的阻塞等待。
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (true){
}
});
t.start();
// 没有超时时间的阻塞等待
t.join();
// 此时main线程就处于 WAITING状态
}
END