温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

java观察者模式怎么避免死锁

发布时间:2026-09-13 05:33:45 来源:亿速云 阅读:86 作者:小樊 栏目:编程语言

在 Java 中使用观察者模式(Observer Pattern)时,容易出现死锁或并发问题,通常不是模式本身的问题,而是通知机制 + 多线程 + 锁使用不当导致的。下面从原因 → 解决方案 → 示例系统说明。


一、为什么观察者模式会死锁?

典型死锁场景:

class Subject {
    private final List<Observer> observers = new ArrayList<>();
    private final Object lock = new Object();

    void addObserver(Observer o) {
        synchronized (lock) {
            observers.add(o);
        }
    }

    void notifyObservers() {
        synchronized (lock) {
            for (Observer o : observers) {
                o.update(this); // ❌ 在锁内回调
            }
        }
    }
}

class ObserverImpl implements Observer {
    private final Subject subject;

    public void update(Subject s) {
        subject.addObserver(...); // ❌ 又去拿 subject 的锁
    }
}

死锁原因:

  • 通知时持有 Subject 锁
  • Observer 在 update() 中又试图获取 Subject 锁
  • 或 Observer 之间互相观察、互相通知

二、避免死锁的核心原则 ✅

✅ 原则 1:通知时不要持有锁

先拷贝观察者列表,再在锁外通知

void notifyObservers() {
    List<Observer> snapshot;
    synchronized (lock) {
        snapshot = new ArrayList<>(observers);
    }
    for (Observer o : snapshot) {
        o.update(this); // ✅ 无锁
    }
}

✅ 避免:

  • 持有锁时回调外部代码
  • 外部代码再反调 Subject

✅ 原则 2:Observer 的 update() 不要修改 Subject

如果必须修改:

  • 使用线程安全队列
  • 或异步处理
public void update(Subject s) {
    executor.submit(() -> {
        // 异步处理,不拿 subject 锁
    });
}

✅ 原则 3:使用 线程安全容器

避免自己加锁出错

private final CopyOnWriteArrayList<Observer> observers =
        new CopyOnWriteArrayList<>();

✅ 优点:

  • 遍历时不会抛 ConcurrentModificationException
  • 不需要显式锁

✅ 原则 4:事件驱动 / 消息队列(推荐)

彻底解耦,避免同步回调

subject.publishEvent(event); // 放入队列

观察者异步消费:

  • BlockingQueue
  • EventBus(Guava / Spring)
  • Reactor / RxJava

三、推荐实现方式(安全版)

✅ 方式 1:CopyOnWriteArrayList(最简单)

class Subject {
    private final List<Observer> observers =
            new CopyOnWriteArrayList<>();

    void add(Observer o) {
        observers.add(o);
    }

    void notifyObservers() {
        for (Observer o : observers) {
            o.update(this);
        }
    }
}

✅ 无锁 ✅ 不会死锁 ⚠️ 写多读少时不合适


✅ 方式 2:同步拷贝 + 无锁通知(通用)

class Subject {
    private final List<Observer> observers = new ArrayList<>();
    private final Object lock = new Object();

    void add(Observer o) {
        synchronized (lock) {
            observers.add(o);
        }
    }

    void notifyAllObservers() {
        List<Observer> copy;
        synchronized (lock) {
            copy = new ArrayList<>(observers);
        }
        for (Observer o : copy) {
            o.update(this);
        }
    }
}

✅ 方式 3:Spring 事件(企业级)

@Component
class MyObserver {
    @EventListener
    public void onEvent(MyEvent e) {
        // 自动异步/同步
    }
}

✅ 内建线程安全 ✅ 解耦彻底


四、死锁自查清单 ✅

  • [ ] 是否在 synchronized 中调用外部方法?
  • [ ] Observer 是否在 update() 中修改 Subject?
  • [ ] 是否使用同一把锁递归获取?
  • [ ] 是否可以用拷贝 + 无锁通知?
  • [ ] 是否可以用异步/事件机制?

五、一句话总结

观察者模式避免死锁的关键:
不在锁中回调 Observer,Observer 不直接操作 Subject,能用异步就用异步。

如果你有具体代码或并发场景(如多线程、UI、Spring),我可以帮你针对性改。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI
助
手