Java Web 服务中的同步

Java synchronization in web service

我有一个 java restful 网络服务程序托管在 tomcat 上。在我的一种 Web 服务方法中,我从 Redis 加载了一个大的对象数组列表(大约 25,000 个条目)。此数组列表每 30 分钟更新一次。一直有多个线程从这个数组列表中读取数据。什么时候,我更新数组列表我想导致最小 disruption/delays 因为可能有其他线程从它读取。

我想知道最好的方法是什么?一种方法是在更新列表的方法中使用 synchronized 关键字。但是,同步方法有开销,因为在更新进行时没有线程可以读取。更新方法本身可能需要几百毫秒,因为它涉及从 redis 读取 + 反序列化。

class WebService {

 ArrayList<Entry> list = new ArrayList<Entry>();

    //need to call this every 30 mins.
    void syncrhonized updateArrayList(){
      //read from redis & add elements to list
    }

    void readFromList(){
      for(Entry e: list) {
       //do some processing
      }
    }

}

更新了最终解决方案: 我最终没有使用显式同步原语。

ArrayList 不是线程安全的。您必须使用矢量 List 使其线程安全。

您也可以通过使用集合来使用线程安全数组列表 Api,但我会推荐向量列表,因为它已经为您提供了您想要的内容。

 //Use Collecions.synzhonizedList method
 List list = Collections.synchronizedList(new ArrayList());
 ...

 //If you wanna use iterator on the synchronized list, use it
 //like this. It should be in synchronized block.
 synchronized (list) {
   Iterator iterator = list.iterator();
   while (iterator.hasNext())
   ...
  iterator.next();
  ...
}

我会建议你通过这个: http://beginnersbook.com/2013/12/difference-between-arraylist-and-vector-in-java/

是否必须与更新的 List 实例相同?您能否每 30 分钟构建一个新列表并替换一个 volatile 引用?

大致如下:

class WebService {
    private volatile List<Entry> theList;

    void updateList() {
        List<Entry> newList = getEntriesFromRedis();
        theList = Collections.unmodifiableList(newList);
    }

    public List<Entry> getList() {
        return theList;
    }
}

这种方法的优点是您不必在其他任何地方进行任何其他同步。

一个 reader-writer 锁(或 Java 中的 ReadWriteLock)就是你所需要的。

一个 reader-writer 锁将允许读取操作并发访问,但写入互斥访问。

看起来像

class WebService {
    final ReentrantReadWriteLock listRwLock = new ReentrantReadWriteLock();
    ArrayList<Entry> list = new ArrayList<Entry>();

    //need to call this every 30 mins.
    void updateArrayList(){
        listRwLock.writeLock().lock();
        try {
            //read from redis & add elements to list
        } finally {
            listRwLock.writeLock().unlock()
        }
    }

    void readFromList(){
        listRwLock.readLock().lock();
        try {
            for(Entry e: list) {
                //do some processing
            }
        } finally {
            listRwLock.readLock().unlock()
        }

    }

}

这是我最终得到的解决方案,

class WebService {

 // key = timeWindow (for ex:10:00 or 10:30 or 11:00), value = <List of entries for that timewindow>
 ConcurrentHashMap<String, List<Entry>> map= new ConcurrentHashMap<String, List<Entry>>();

    //have setup a timer to call this every 10 mins.
    void updateArrayList(){
     // populate the map for the next time window with the corresponding entries. So that its ready before we start using it. Also, clean up the expired entries for older time windows.

    }

    void readFromList(){
      list = map.get(currentTimeWindow)
      for(Entry e: list) {
       //do some processing
      }
    }

}