多线程基础保姆级教程
多线程基础
- 中断一个线程
- **线程等待**
- 获取当前线程引用
- 休眠当前线程
- 观察线程的所有状态
- 线程状态和状态转移的意义
中断一个线程
通常使用Thread.currentThread().isInterrupted()
和 t.isInterrupted()
配合使用来中断一个线程
currentThread()方法用来获取当前线程的是咧,哪个线程调用就返回哪个线程的对象。
Thread内部有一个标志位,这个标志位就可以用来判断线程是否结束
上篇讲到了,手动设置标志位,当线程内部在sleep的时候,主线程修改变量,新线程不能及时响应
而t.interrupt()就可以把Thread内部的这个标志位设置位true,及时线程内部的逻辑出现阻塞(sleep),也是可以用这个方法唤醒的。
正常来说,sleep会休眠到时间到,才能唤醒。此处给出的interrupt就可以使sleep内部触发一个异常,从而提前被唤醒。而我们自己设置的标志位无法实现这个效果。
例子
在这里插入代码package thread;
public class Demo001 {
public static void main(String[] args) {
Thread t = new Thread(()->{
//Thread头部有一个现成的标志位,可以用来判定当前循环是否结束
while(!Thread.currentThread().isInterrupted()){
System.out.println("线程工作中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("让t终止");
t.isInterrupted();
}
}
运行结果
运行结果显示,异常确实是出现了,sleep确实唤被醒了,但是上述t仍然在继续工作!!并没真正结束!!!
interrupt()虽然唤醒线程了,此时sleep()方法会抛出异常,同时会自动清除刚才设置的标志位。这样就使得 设置标志位这样的效果好像没生效一样。
为啥这么设定??
java这样设定是期望,当线程收到 要中断 这样的信号时候,它能够自由决定,接下来该怎么处理?
小例子:
有一天,我正在打游戏,我吗让我下楼买瓶酱油
当我收到这个信号,就有三种做法
1.直接丢下游戏,去买酱油
2.我把这局打完再去
3.直接忽略,假装没听见
同样的线程也可以采取这三种方式来执行
这样的目的是为了让线程有更多的 可操作空间。而这种可操作空间的前提是通过异常(异常可以清楚设置好的标志位,从而给了线程更多可操作的空间)。如果没有sleep(),没有抛出异常,就没有上述的可操作空间。
线程等待
join()方法是让一个线程等待另一个线程执行结束再继续执行,一般来说,等待操作都是要有一个超时时间的。本质上就是控制线程的执行顺序
t.join()的工作过程
(1)如果t线程正在运行,此时调用join的线程就会阻塞,一直阻塞到t线程执行结束为止
(2)如果t1线程已经执行结束了,此时调用join线程,就直接返回了,不会涉及到阻塞
public void join(long millis) 最多等 millis 毫秒,
public void join(long millis, int nanos) 同理,但可以更高精度
实际开发中一般不建议死等,最好要带有"超时时间"
获取当前线程引用
例子
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}
休眠当前线程
注意:因为线程的调度是不可控的,所以,这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的。 例如:sleep(1000),
系统会按照1000这个时间来控制让线程休眠 但是当1000时间到了之后,系统会唤醒这个线程.(阻塞->就绪)
但是不是说这个线程成了就绪状态,就能立即回到cpu上运行??(这中间有一个“调度”开销)
对于windows或者linux这样的系统来说,调度开销很大,可能达到ms级别.
有些场景,可能对于时间精度要求是很高的比如,发射卫星;或者导弹拦截 往往需要使用"实时操作系统",任务调度的开销在一定时间范围之内.
为了实时,也有很多的限制,功能上是不如windows和linux的.
观察线程的所有状态
线程的状态是一个枚举类型 Thread.State
public class ThreadState {
public static void main(String[] args) {
for (Thread.State state : Thread.State.values()) {
System.out.println(state);
}
}
}
线程状态和状态转移的意义
大家不要被这个状态转移图吓到,我们重点是要理解状态的意义以及各个状态的具体意思。
NEW:Thread对象已经有了.start方法还没调用
TERMINATED:Thread对象还在,内核中的线程已经没了.
RUNNABLE:就绪状态(线程已经在cpu上执行了/线程正在排队等待上cpu执行)
TIMED_WAITING:阻塞.由于sleep这种固定时间的方式产生的阻塞
WAITING:阻塞.由于wait这种不固定时间的方式产生的阻塞
BLOCKED:阻塞.由于锁竞争导致的阻塞
注意:
BLOCKED 表示等待获取锁, WAITING 和 TIMED_WAITING 表示等待其他线程发来通知.
TIMED_WAITING 线程在等待唤醒,但设置了时限; WAITING 线程在无限等待唤醒