前言
小结一下常见的Thread关键点
线程启动与运行
三种方法
方法一,直接使用Thread
new Thread(){
@Override
public void run() {
System.out.println("第一种方法");
}
}.start();
方法二,使用Runnable配合Thread
new Thread(()->{
System.out.println("第二种方法");
}).start();
方法三,FutureTask(Callable)配合Thread
new Thread(new FutureTask<Integer>(()->{
System.out.println("第三种方法");
return null;
})).start();
关系
-
run()
为任务,start()
为线程启动- run()方法由Runnable接口定义
- Thread之所以有run()方法是因为实现了Runnable接口
-
Thread的运行机制为
- start()启动线程,会执行run()任务
- 启动线程不代表立刻执行,需要等CPU调度才能执行,只是进入就绪态
-
第一种方法,意味着Thread通过子类重写了run
-
第二种,意味着运行通过参数传进来的run任务
-
第三种,FutureTask也实现了Runnable接口,其run方法会执行Callable的call方法并存储结果,同时实现了Future接口,用get方法就能够获取存储的结果,具体之前FutureTask源码解析时说过了,此时的具体任务是写在Callable的call方法中,但实际Thread还是执行run方法
线程打断
我们只需要这么去理解,Thread中有个状态变量称为打断状态
- interrupt() :将当前线程的打断状态设置为True
- isInterrupted():获取当前线程的打断状态
- interrupted():获取当前线程的打断状态,获取完后将打断状态变为False
如果我们要打断一个运行的线程,怎么打断?
- main线程调用 interrupt()修改打断状态
- 运行的子线程不断调用isInterrupted()判断是否自己被打断,如果被打断则break
- 要注意的是, interrupt()并没有主动中断线程运行,只是修改了状态值,意味着如果子线程不进行状态判断,是不会被打断的
private static void test2() throws InterruptedException {
Thread t2 = new Thread(() -> {
while (true) {
Thread current = Thread.currentThread();
boolean interrupted = current.isInterrupted();
if (interrupted) {
System.out.println(" 打断状态: {}" + interrupted);
break;
}
}
}, "t2");
t2.start();
sleep0(1);
t2.interrupt();
}
那有人会问了,为什么打断阻塞状态的线程时(如下demo),不需要自己判断状态呢?
- 自己都阻塞没法运行了是不能自己判断状态的
- 只能交由操作系统解决,打断逻辑应该还是和上面一样
private static void test1() throws InterruptedException {
Thread t1 = new Thread(() -> {
sleep0(10);
}, "t1");
t1.start();
sleep0(1);
t1.interrupt();
System.out.println(" 打断状态: {}" + t1.isInterrupted());
}
- 某个线程在阻塞状态被打断不会影响整个项目的运行,所以交给操作系统去打断没有问题
- 那么对于正在运行的线程,以上述方法打断是不行的,因为可能要释放一些资源不然会导致死锁,这部分可以看看ReentryLock怎么实现可打断的,前面我也已经分析过了
- 因此我们需要优雅地打断
class TPTInterrupt {
private Thread thread;
public void start() {
thread = new Thread(() -> {
while (true) {
Thread current = Thread.currentThread();
if (current.isInterrupted()) {
log.debug("料理后事");
break;
}
try {
Thread.sleep(1000);
log.debug("将结果保存");
} catch (InterruptedException e) {
current.interrupt();
}
// 执行监控操作
}
}, "监控线程");
thread.start();
}
public void stop() {
thread.interrupt();
}
}
当然,也可以自定义一个标志位来代替打断状态,注意标志位必须是volatile修饰的,保证可见性
class TPTVolatile {
private Thread thread;
private volatile boolean stop = false;
public void start() {
thread = new Thread(() -> {
while (true) {
Thread current = Thread.currentThread();
if (stop) {
log.debug("料理后事");
break;
}
try {
Thread.sleep(1000);
log.debug("将结果保存");
} catch (InterruptedException e) {
}
// 执行监控操作
}
}, "监控线程");
thread.start();
}
public void stop() {
stop = true;
thread.interrupt();
}
}