如何优雅结束一个线程

2021/2/20 Java多线程

# 1.自然结束

能自然结束的话,尽量让一个线程自然结束。

# 2.配合自定义volatile标志位

# 示例: volatile stop

// volatile保证可见性
private static volatile boolean isStopped = false;

public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(() -> {
        while (!isStopped) {
            // 事实上,synchronized的调用会导致本地内存同步
            // println中调用了synchronized,此时将isStopped的volatile去除
            // 该线程依然可以正常退出
            System.out.println("Do something");
        }
        System.out.println("Thread exited");
    });

    thread.start();
    Thread.sleep(1000);
    isStopped = true;
}
// Do something
// Do something
// Do something
// Do something
// Thread exited
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 缺点

循环期间或线程执行中做了阻塞操作,不能及时查询volatile标志位,导致线程结束延迟。

# 2.使用Interrupt

# Thread中与interrupt相关的三个方法

//t.interrupt() 实际上只是设置t线程的打断标志位为true,并不是直接打断线程的运行
public void interrupt() 

//t.isInterrupted() 仅返回当前线程的打断标志位
public boolean isInterrupted()    

//Thread.interrupted() 返回当前线程的打断标志位,并将该标志位复位,即重新设置为false
public static boolean interrupted()
1
2
3
4
5
6
7
8

# interrupt与sleep(), wait(), join()

sleep()方法在睡眠的时候,不到时间是没有办法叫醒的,这个时候可以用interrupt设置标志位,然后需要catch InterruptedException来进行处理,决定继续睡或者是执行其他逻辑。

注意,在catch到该异常时,中断标志位已经由JVM复位。

# 示例: 打断sleep()

public static void main(String[] args) throws InterruptedException {

    Thread thread = new Thread(() -> {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            System.out.println("Interrupted: " + Thread.currentThread().isInterrupted());
        }
    });

    thread.start();
    thread.interrupt();
}
//Interrupted: false
1
2
3
4
5
6
7
8
9
10
11
12
13
14

注意,interrupt()不能中断正在竞争synchronized锁的线程。
如果希望在竞争锁的阻塞状态下可以被打断,需要使用ReentrantLock的lockInterruptibly()

# 示例: lockInterruptibly()

static ReentrantLock lock = new ReentrantLock();

public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        try {
            lock.lock();
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    });

    t1.start();
    Thread.sleep(1000);

    Thread t2 = new Thread(() -> {
        System.out.println("t2 started");
        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            System.out.println("t2 interrupted");
        } finally {
            try {
                lock.unlock();
            } catch (Exception e) {
            }
        }
        System.out.println("t2 ended");
    });

    t2.start();
    Thread.sleep(1000);

    t2.interrupt();
}
// t2 started
// t2 interrupted
// t2 ended
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40