当前位置: 代码网 > it编程>编程语言>Java > Java并发编程中高频异常的原因,场景与解决方案全解析

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

2026年03月09日 Java 我要评论
在 java 并发编程中,异常处理是保障程序稳定性的核心环节。新手开发者常常会被interruptedexception、illegalmonitorstateexception等并发相关异常困扰,这

在 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. 解决方案

  • 捕获异常后重置中断状态:异常会清除线程的中断标记,需调用thread.currentthread().interrupt()恢复,避免后续逻辑无法感知中断。
  • 优雅处理中断:不要吞掉异常,根据业务场景决定是否终止线程(如任务取消时)。

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

1. 异常本质

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

2. 触发场景

  • 场景 1:未在synchronized块内调用wait()/notify()
  • 场景 2:synchronized锁定的对象 ≠ 调用wait()的对象;
  • 场景 3:混用lockcondition.await()/signal())和synchronizedwait()/notify())。

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. 解决方案

  • 单线程场景:迭代时修改集合需使用迭代器的remove()方法,而非集合的remove()
  • 多线程场景
    • 方案 1:使用线程安全集合(如copyonwritearraylistconcurrenthashmap);
    • 方案 2:对非线程安全集合加锁(如synchronizedreentrantlock);
    • 方案 3:使用collections.synchronizedlist(new arraylist<>())包装集合(注意:迭代时仍需手动加锁)。

正确示例(copyonwritearraylist)

// 线程安全的arraylist替代方案
private static final list<integer> list = new copyonwritearraylist<>();

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

1. 异常本质

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

2. 触发场景

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

  • 核心线程数:corepoolsize
  • 最大线程数:maximumpoolsize
  • 任务队列容量:workqueue.size()
  • 最大可处理任务数 = maximumpoolsize + workqueue.size()

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. 解决方案

  • 合理配置线程池参数:根据业务场景调整corepoolsizemaximumpoolsize、队列容量,避免任务堆积;
  • 选择合适的拒绝策略
    • abortpolicy:默认,抛异常(适合核心任务,需感知任务拒绝);
    • callerrunspolicy:由提交任务的线程执行(降级处理,避免任务丢失);
    • discardpolicy:静默丢弃任务(非核心任务);
    • discardoldestpolicy:丢弃队列最老的任务,尝试提交新任务;
  • 异步降级:结合消息队列(如 rabbitmq)削峰填谷,避免瞬时任务量超过线程池承载能力。

优化示例(使用 callerrunspolicy)

threadpoolexecutor executor = new threadpoolexecutor(
    2, 5, 60l, timeunit.seconds,
    new arrayblockingqueue<>(10),
    new threadpoolexecutor.callerrunspolicy() // 提交线程执行被拒绝的任务
);

总结

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

  • interruptedexception:阻塞方法被中断时抛出,捕获后需重置中断状态,避免吞掉异常;
  • illegalmonitorstateexception:调用 wait/notify 前必须持有对象的监视器锁,lock 需搭配 condition 使用;
  • concurrentmodificationexception:非线程安全集合并发修改触发,优先使用 copyonwritearraylist/concurrenthashmap;
  • rejectedexecutionexception:线程池任务超限触发,需合理配置参数 + 选择合适的拒绝策略。

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

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com