了解侦听器通知程序上的同步

时间:2016-10-15 09:48:56

标签: java multithreading synchronized

我有单例线程类,有时会调用下面的函数,并通过它的线程run()方法通知侦听器:

public class Serial implements Runnable
{
    private ArrayList observers = new ArrayList();
    ...

    public void run()
    {
    notifyListeners(new CS());
    }

    public synchronized void notifyListeners(CS value)
    {
        log.debug("notifying listeners with Control ");
        int os = observers.size();
        for (int i = 0; i < observers.size(); i++)
            {
                MListener observer = (MListener) observers.get(i);
                observer.dataReceived(value);
            }
    }
    ...

    public void addListener(MListener lsn)
    {
    observers.add(lsn);
    }

    public void removeListener(MListener lsn)
    {
    observers.remove(lsn);
    }


}

我想知道在synchronized方法上提供notifyListeners的内容是什么?其中一个原因 - 在调用ArrayList observers时不允许从/ notifyListeners添加/删除观察者。如果我错了,请纠正我。还有什么可以给的?

UPD

我已使用两种方法addListenerremoveListener更新了我的代码。我认为这是错误的,因为这两个方法都不是synchronized,可能是从另一个线程调用的?

2 个答案:

答案 0 :(得分:1)

IMO同步通知没有意义。如果我理解正确,那么notify方法只能由你的单例线程调用。

但您的观察者实现内容可能可能被不同的线程访问。必须同步对内部状态的访问。

E.g。如果要将给定值保存到观察者的成员,稍后由例如主GUI线程必须同步对该成员的访问:

// called by your notify thread
void dataReceived( CS value)
{
    synchronized (this)
    {
        myValue = value;
    }
}

// called by your GUI main thread:
public CS getValue()
{
    synchronized (this)
    {
        // optional check for not null:
        if ( myValue == null) throw new IllegalStateException();
        logger.debug( "returning value: " + myValue);
        return myValue;
    }
}

如果CS是AtomicXY(例如AtomicInteger)类,则不需要同步。但是,如果您想要做的不仅仅是分配/返回值(例如某些检查或日志输出),则必须进行同步。

答案 1 :(得分:0)

答案主要取决于您的背景,因为它可能有两个潜在原因:

  1. 我们希望保护您的侦听器列表免受并发访问和修改,因为它是ArrayList,它不是线程安全的(假设列表可以在代码中的其他地方进行修改,并且只要它被修改它受synchronized块的保护,this作为对象的监视器,否则它将无法正常完成和/或无用)。
  2. 我们希望阻止某些内部逻辑的并发通知。
  3. 在您的情况下,因为您只有一个调用notifyListeners#2的线程不应该是原因,除非代码所有者假定您将来可以有多个线程执行此任务。

    只有当观察者列表可以被其他线程同时修改时,#1才有意义,如果不可能你只能从你的方法声明中删除关键字synchronized,因为它会没用影响表演一无所获。

    假设#1是原因,您应该使用线程安全的List CopyOnWriteArrayList而不是ArrayList,因为它在大多数读取访问方案中非常有效我们主要阅读并很少修改的观察者列表的情况,你的代码将是这样的:

    public class Serial implements Runnable {
        private final List<MListener> observers = new CopyOnWriteArrayList<>();
        ...
        public void notifyListeners(CS value) {
            log.debug("notifying listeners with Control ");
            for (MListener observer : observers) {
                observer.dataReceived(value);
            }
        }
    
      

    我认为这是错误的,因为这两种方法都没有   synchronized并且可能从另一个线程调用?

    我确认当前代码不正确,因为observers方法publicaddListener并且这些方法可以通过并发线程修改removeListener列表请勿在{{1​​}}块中使用synchronized作为对象监视器修改您的列表,以便当前代码不是线程安全的,因为它不会&# 39;防止并发访问非线程安全列表。