MST
星途 面试题库

面试题:Java多线程编程中线程中断机制在锁竞争场景下的处理

当一个线程持有锁并正在执行临界区代码,此时另一个线程发起中断请求,如何确保锁的正确释放以及数据一致性?请结合`ReentrantLock`和`synchronized`两种锁机制详细说明处理思路和可能遇到的问题。
47.6万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

使用synchronized

  1. 处理思路
    • synchronized是基于JVM的内置锁。当一个线程获取到synchronized锁进入临界区时,另一个线程发起中断请求,被中断线程在try - catch块中捕获到中断异常InterruptedException。在catch块中,由于try块内代码已经持有synchronized锁,当执行完catch块逻辑(例如记录日志、进行一些清理操作)后,线程退出try - catch块,此时synchronized锁会自动释放,从而确保锁的正确释放。同时,synchronized保证了同一时刻只有一个线程能进入临界区,所以数据一致性在锁的作用下得以维持。
    • 示例代码如下:
public class SynchronizedInterruptExample {
    private static final Object lock = new Object();
    public static void main(String[] args) {
        Thread mainThread = Thread.currentThread();
        Thread interruptingThread = new Thread(() -> {
            try {
                Thread.sleep(1000);
                mainThread.interrupt();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        interruptingThread.start();
        synchronized (lock) {
            try {
                System.out.println("Main thread entered critical section");
                while (!Thread.currentThread().isInterrupted()) {
                    // 模拟临界区操作
                    Thread.sleep(500);
                }
            } catch (InterruptedException e) {
                System.out.println("Main thread interrupted");
                // 可以在这里进行一些清理操作
            }
        }
        System.out.println("Main thread released lock");
    }
}
  1. 可能遇到的问题
    • 如果在catch块中没有正确处理中断(例如忽略中断异常),线程可能不会正确响应中断,导致锁不能及时释放,影响系统的并发性能。
    • synchronized锁是不可中断的,一旦线程获取到锁进入临界区,除非线程正常执行完临界区代码或者抛出未捕获的异常,否则其他线程无法中断它,这可能导致在某些需要及时响应中断的场景下出现问题。

使用ReentrantLock

  1. 处理思路
    • ReentrantLock提供了可中断的锁获取方式lockInterruptibly()。当一个线程通过lockInterruptibly()获取到锁并进入临界区时,另一个线程发起中断请求,被中断线程会在执行lockInterruptibly()处或者在临界区内抛出InterruptedException。在捕获到该异常后,线程可以在catch块中调用unlock()方法来手动释放锁,从而确保锁的正确释放。同时,ReentrantLock通过独占锁的机制保证同一时刻只有一个线程能进入临界区,维持数据一致性。
    • 示例代码如下:
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockInterruptExample {
    private static final ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        Thread mainThread = Thread.currentThread();
        Thread interruptingThread = new Thread(() -> {
            try {
                Thread.sleep(1000);
                mainThread.interrupt();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        interruptingThread.start();
        try {
            lock.lockInterruptibly();
            System.out.println("Main thread entered critical section");
            while (!Thread.currentThread().isInterrupted()) {
                // 模拟临界区操作
                Thread.sleep(500);
            }
        } catch (InterruptedException e) {
            System.out.println("Main thread interrupted");
            // 释放锁
            lock.unlock();
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        System.out.println("Main thread released lock");
    }
}
  1. 可能遇到的问题
    • 如果在catch块中忘记调用unlock()方法释放锁,会导致锁永远不会被释放,其他线程无法获取锁,造成死锁。
    • 由于ReentrantLock需要手动释放锁,在复杂的业务逻辑中,如果unlock()调用的位置不正确(例如在异常处理中没有正确释放锁,或者在多个try - catch块中有遗漏),也可能导致锁释放问题,影响数据一致性和系统并发性能。