单例线程安全列表的实现

时间:2013-12-02 11:25:40

标签: java multithreading spring concurrency singleton

我正在使用Spring框架。需要有一个对象列表,它应该立即从数据库中获取所有数据。更改数据时,list将为null,下一次get操作应再次填充数据库中的数据。我的代码是否适用于多线程环境?

@Component
@Scope("singleton")
public class MyObjectHolder {
    private volatile List<MyObject> objectList = null;

    public List<MyObject> getObjectList() {
        if (objectList == null) {
            synchronized (objectList) {
                if (objectList == null) {
                    objectList = getFromDB();
                }
            }
        }
        return objectList;
    }

    synchronized
    public void clearObjectList() {
        objectList = null;
    }
}

2 个答案:

答案 0 :(得分:2)

简答:不。

public class MyObjectHolder {
  private final List<MyObject> objectList = new List<>();
  public List<MyObject> getObjectList() {          
    return objectList;
  }

这是首选的单身人士模式。

现在您需要弄清楚如何以线程安全的方式将数据放入列表中。对于这个Java,在并发包中已经有一些预制的线程安全列表,它应该优先于任何同步的实现,因为它们在繁重的线程下更快。

你的问题可以这样解决:

public class MyObjectHolder {

  private final CopyOnWriteArrayList<MyObject> objectList = new CopyOnWriteArrayList<>();

  public List<MyObject> getObjectList() {
    return objectList;
  }

  public boolean isEmtpy() {
    return objectList.isEmpty();
  }

  public void readDB() {
    final List<MyObject> dbList = getFromDB();
    // ?? objectList.clear();
    objectList.addAll(dbList);
  }
}

请注意没有任何同步,但事情是完全线程安全的。 Java保证该列表上的调用以原子方式执行。因此,当其他人填写列表时,我可以致电isEmpty()。我只会得到一个时刻的快照,不知道我会得到什么结果,但它会在所有情况下成功而没有错误。

首先将DB调用写入临时列表,因此此处不会发生线程问题。然后addAll()原子地再次将内容移动到真实列表中:所有线程安全。

最糟糕的情况是,线程A刚刚写完新数据,而同时线程B检查列表是否包含任何元素。线程B将接收列表为空的信息,但稍后它会包含大量数据。您需要通过重复轮询或使用观察者模式来通知其他线程来处理这种情况。

答案 1 :(得分:1)

不,您的代码不是线程安全的。例如,您可以在时间X在一个线程中分配objectList,但在时间X + 1将其设置为空(通过clearObjectList()),因为您正在同步2个不同的对象。第一次同步在objectList本身,第二次同步在MyObjectHolder的实例上。您应该在使用共享资源而不是使用synchonize时查看锁定,特别是ReadWriteLock之类的内容。