多线程系列-2 线程中断机制

线程中断总结

1.线程中断API

理解线程中断的本质,可以从"如果没有线程中断机制,会有什么后果"的角度考虑: 因可能引发数据不同步以及资源无法回收问题,通过stop暴力停止线程的方法已被废弃;线程被CPU调度后会独立发展和自治, 无法解决如线程长期阻塞后无法恢复等问题。
因此,中断机制是为了引入从线程外部进行干预的能力。

"没有任何语言方面的需求要求一个被中断的程序应该终止。中断一个线程只是为了引起该线程的注意,被中断线程可以决定如何应对中断 "

线程被中断时, 在JVM中该线程会收到一个中断信号(并将线程标记为中断状态),由线程自己决定是否处理中断。常用线程API中,部分阻塞(wait/sleep/join/future等)会响应中断——抛出中断异常并清理中断状态,部分会直接忽略中断。

Java为线程中断提供了3个API:

// 静态方法,返回当前线程是否中断&&清理中断状态
public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}

// 返回线程的中断状态
public boolean isInterrupted() {
    return isInterrupted(false);
}

// 将线程标记为中断状态
public void interrupt() {
    if (this != Thread.currentThread())
        checkAccess();

    synchronized (blockerLock) {
        Interruptible b = blocker;
        if (b != null) {
            interrupt0();           // Just to set the interrupt flag
            b.interrupt(this);
            return;
        }
    }
    interrupt0();
}

2.sleep和wait被中断

当线程因wait和sleep()方法阻塞后,被中断时会抛出InterruptedException中断异常,以下通过案例进行介绍。

//为了简化Demo代码的try-catch逻辑,添加一个工具类
public class ThreadUtils {
    public static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

sleep案例:

public class SleepDemo {
    public static void main(String[] args) {
        // subThread线程休眠60s
        Thread subThread = new Thread(() -> ThreadUtils.sleep(60 * 1000));
        subThread.start();
        
        ThreadUtils.sleep(1000);
        // main线程中调用subThread的interrupt方法,打断subThread线程
        subThread.interrupt();
    }
}

运行结果如下:

java.lang.InterruptedException: sleep interrupted
 at java.lang.Thread.sleep(Native Method)
 at com.testthread.ThreadUtils.sleep(ThreadUtils.java:10)
 at com.testthread.SleepDemo.lambda$main$0(SleepDemo.java:9)
 at java.lang.Thread.run(Thread.java:745)

Process finished with exit code 0

wait案例:

public class WaitDemo {
    private static Object lock = new Object();

    public static void main(String[] args) {
        Thread subThread = new Thread(() -> {
            synchronized (lock) {
                try {
                    // subThread线程调用wait方法阻塞
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        subThread.start();

        ThreadUtils.sleep(1000);
        // main线程中调用subThread的interrupt方法,打断subThread线程
        subThread.interrupt();
    }
}

运行结果如下所示:

java.lang.InterruptedException
 at java.lang.Object.wait(Native Method)
 at java.lang.Object.wait(Object.java:502)
 at com.testthread.WaitDemo.lambda$main$0(WaitDemo.java:14)
 at java.lang.Thread.run(Thread.java:745)

顺便提一下:sleep是抱着锁休眠的,wait阻塞前会释放锁。

3.IO中断

常用的IO操作不会响应中断,以下通过案例进行介绍。

public class IODemo {
    private static Object lock = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (lock) {
                Socket socket = new Socket();
                try {
                    socket.connect(new InetSocketAddress("127.0.0.1", 9999));
                    InputStream inputStream = socket.getInputStream();
                    System.out.println("[Thread-1] Begin to read...");
                    // 因没有可读数据,thread1持续处于阻塞状态
                    inputStream.read();
                    System.out.println("[Thread-1] Read finish.");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        thread1.start();
        ThreadUtils.sleep(1000);


        Thread thread2 = new Thread(() -> {
            // thread1获取锁后阻塞且不释放锁,thread2因获取不到锁,处于阻塞状态
            synchronized (lock) {
                System.out.println("[Thread-2] Begin to exec subThread-2...");
            }
        });
        thread2.start();

        System.out.println("[Main] Begin to set thread interrupt...");
        // main线程中调用thread1的interrupt方法,打断thread1线程
        thread1.interrupt();
        System.out.println("[Main] Success to set thread interrupt.");
    }
}

程序持续处于阻塞状态,运行结果如下所示:

[Thread-1] Begin to read...
[Main] Begin to set thread interrupt...
[Main] Success to set thread interrupt.

此案例还说明了,IO阻塞时,不会释放当前线程占有的锁。

4.FutureTask.get()阻塞中断

FutureTask提供了两个阻塞获取结果的方法:get(long timeout, TimeUnit unit)get(). 前者超时后会抛出TimeoutException异常,从阻塞中恢复,后者会一直阻塞; 二者对中断的响应处理相同(抛出中断异常),以get()为例进行介绍。

public class FutureTaskDemo {
    private static ExecutorService executorService = Executors.newSingleThreadExecutor();
    public static void main(String[] args) {
        FutureTask futureTask = new FutureTask(() -> {
            // futureTask阻塞10秒
            ThreadUtils.sleep(1000 * 10);
            return new Object();
        });
        executorService.submit(futureTask);
        Thread subThread = new Thread(() -> {
            try {
                // subThread获取futureTask的运行结果而阻塞
                futureTask.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });
        subThread.start();

        ThreadUtils.sleep(1000);
        // main线程中调用subThread的interrupt方法,打断subThread线程
        subThread.interrupt();
    }
}

运行结果如下所示:

java.lang.InterruptedException
 at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:404)
 at java.util.concurrent.FutureTask.get(FutureTask.java:191)
 at com.testthread.FutureTaskDemo.lambda$main$1(FutureTaskDemo.java:21)
 at java.lang.Thread.run(Thread.java:745)

5.Locksupport.park()被中断

Locksupport.park()阻塞的线程被中断时,会被唤醒并标记为中断状态,而不会抛出异常。案例如下所示:

public class LockSupportDemo {
    public static void main(String[] args) {
        Thread subThread = new Thread(() -> {
            System.out.println("[subThread-begin] state is "+Thread.currentThread().isInterrupted());
            // 调用LockSupport.park()阻塞当前线程
            LockSupport.park();
            System.out.println("[subThread-end] state is "+Thread.currentThread().isInterrupted());
        });
        subThread.start();
        ThreadUtils.sleep(1000);

        System.out.println("[Main] begin to interrupt subThread.");
        // main线程中调用subThread的interrupt方法,打断subThread线程
        subThread.interrupt();
//        LockSupport.unpark(subThread);
        System.out.println("[Main] Success to interrupt subThread.");
    }
}

运行结果如下所示:

[subThread-begin] state is false
[Main] begin to interrupt subThread.
[Main] Success to interrupt subThread.
[subThread-end] state is true

当线程因LockSupport.park()阻塞,当该线程被中断时,线程会被唤醒并标记为中断状态(JUC中经常使用)。

6.JUC其他API

JUC常见的AIP还有Lock、Condition、BlockingQueue等。

6.1 Lock

public interface Lock {
    void lockInterruptibly() throws InterruptedException;
    
    void lock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
}

Lock中lock()不响应中断,lockInterruptibly响应中断(抛出InterruptedException异常)。

6.2 Condition

public interface Condition {
    void awaitUninterruptibly();
    
    void await() throws InterruptedException;
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    boolean awaitUntil(Date deadline) throws InterruptedException;

}

Condition中:awaitUninterruptibly不响应中断,其他均响应中断(抛出InterruptedException异常).

6.3 BlockingQueue

public interface BlockingQueue<E> extends Queue<E> {
    void put(E e) throws InterruptedException;
 E take() throws InterruptedException;
 
    boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException;    
    E poll(long timeout, TimeUnit unit) throws InterruptedException;  
}

BlockingQueue中所有阻塞方法均响应中断(抛出InterruptedException异常).

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值