java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java并发编程高频异常

Java并发编程中高频异常的原因,场景与解决方案全解析

作者:Akk_it

在Java并发编程中,异常处理是保障程序稳定性的核心环节,本文将深度解析4类高频并发异常的产生原因、典型场景,并给出可落地的解决方案,帮你彻底避开这些坑

在 Java 并发编程中,异常处理是保障程序稳定性的核心环节。新手开发者常常会被InterruptedExceptionIllegalMonitorStateException等并发相关异常困扰,这些异常不仅定位困难,还可能导致程序死锁、数据错乱甚至服务崩溃。本文将深度解析 4 类高频并发异常的产生原因、典型场景,并给出可落地的解决方案,帮你彻底避开这些 “坑”。

一、java.lang.InterruptedException(中断异常)

1. 异常本质

InterruptedException受检异常,表示线程在执行阻塞操作(如sleep()wait())时被其他线程中断,导致阻塞状态被强制打断。

2. 触发场景

当线程调用Thread.sleep(long)Object.wait()Thread.join()等阻塞方法时,其他线程调用该线程的interrupt()方法,就会触发此异常。

3. 错误示例

public class InterruptDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                // 线程休眠10秒
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                System.out.println("线程休眠被中断:" + e.getMessage());
                // 重置中断状态(关键!)
                Thread.currentThread().interrupt();
            }
        });
        t1.start();
        
        // 1秒后中断t1线程
        Thread.sleep(1000);
        t1.interrupt();
    }
}

4. 解决方案

二、java.lang.IllegalMonitorStateException(非法监视器状态异常)

1. 异常本质

调用wait()notify()notifyAll()时,当前线程未持有该对象的监视器锁(即未进入synchronized块 / 方法),或锁对象与调用方法的对象不一致。

2. 触发场景

3. 错误示例 vs 正确示例

错误示例(未加 synchronized)

public class IllegalMonitorDemo {
    private static final Object lock = new Object();
    
    public static void main(String[] args) {
        // 直接调用wait(),未持有lock的锁
        lock.wait(); // 抛出IllegalMonitorStateException
    }
}

正确示例

public class IllegalMonitorDemo {
    private static final Object lock = new Object();
    
    public static void main(String[] args) throws InterruptedException {
        synchronized (lock) { // 必须先获取lock的监视器锁
            lock.wait(); // 正确:当前线程持有lock的锁
        }
    }
}

4. 解决方案

核心原则:调用wait()/notify()的代码必须在synchronized块 / 方法内,且锁定的对象必须是调用这些方法的对象;

Lock+Condition 用法:若使用ReentrantLock,需通过Condition.await()/signal()替代wait()/notify(),且调用前需获取Lock锁:

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock(); // 获取锁
try {
    condition.await(); // 替代wait()
} finally {
    lock.unlock(); // 释放锁
}

三、java.util.ConcurrentModificationException(并发修改异常)

1. 异常本质

单线程下迭代集合时修改集合(如 add/remove),或多线程同时修改非线程安全集合(如ArrayList),导致迭代器检测到集合结构被意外修改。

2. 触发场景

ArrayListHashMap等集合的方法(add()remove())未加锁,多线程并发修改时,迭代器的modCount(修改次数)与expectedModCount不一致,触发异常。

3. 错误示例(多线程修改 ArrayList)

public class ConcurrentModificationDemo {
    private static final List<Integer> list = new ArrayList<>();
    
    public static void main(String[] args) {
        // 线程1:循环添加元素
        new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                list.add(i);
            }
        }).start();
        
        // 线程2:循环迭代并修改
        new Thread(() -> {
            for (Integer num : list) { // 迭代时触发检查
                list.remove(num); // 抛出ConcurrentModificationException
            }
        }).start();
    }
}

4. 解决方案

正确示例(CopyOnWriteArrayList)

// 线程安全的ArrayList替代方案
private static final List<Integer> list = new CopyOnWriteArrayList<>();

四、java.util.concurrent.RejectedExecutionException(拒绝执行异常)

1. 异常本质

向线程池提交任务时,线程池已达到最大处理能力(核心线程 + 非核心线程 + 任务队列均满),且拒绝策略为AbortPolicy(默认),导致任务被拒绝执行。

2. 触发场景

线程池参数配置不合理,提交的任务数超过其最大容量:

3. 错误示例(任务数超过线程池容量)

public class RejectedExecutionDemo {
    public static void main(String[] args) {
        // 配置线程池:核心2,最大5,队列10,拒绝策略AbortPolicy
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 5, 60L, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(10),
            new ThreadPoolExecutor.AbortPolicy() // 默认拒绝策略:直接抛异常
        );
        
        // 提交16个任务(最大容量5+10=15),第16个被拒绝
        for (int i = 0; i < 16; i++) {
            int finalI = i;
            executor.submit(() -> {
                try {
                    Thread.sleep(1000);
                    System.out.println("执行任务:" + finalI);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }
        executor.shutdown();
    }
}

4. 解决方案

优化示例(使用 CallerRunsPolicy)

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    2, 5, 60L, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(10),
    new ThreadPoolExecutor.CallerRunsPolicy() // 提交线程执行被拒绝的任务
);

总结

本文梳理了 Java 并发编程中 4 类高频异常的核心要点,关键总结如下:

以上就是Java并发编程中高频异常的原因,场景与解决方案全解析的详细内容,更多关于Java并发编程高频异常的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文