我赞同 Telcontar对数据库的建议,因为它们实际上是为管理这种规模的数据和线程之间的协商而设计的,而内存中的集合则不是。
您说数据在服务器上的数据库中,客户端上的本地列表是为了用户界面。您不需要一次将所有 100000 个项目保留在客户端上,或者对其执行如此复杂的编辑。在我看来,您在客户端上想要的是数据库上的轻量级缓存。
编写一个缓存,一次只存储客户端上的当前数据子集。此客户端缓存不对自己的数据执行复杂的多线程编辑;相反,它将所有编辑内容提供给服务器,并监听更新。当服务器上的数据发生变化时,客户端会简单地忘记旧数据并重新加载它。只允许一个指定线程读取或写入集合本身。这样,客户端只需镜像服务器上发生的编辑,而不需要复杂的编辑本身。
是的,这是一个相当复杂的解决方案。它的组成部分是:
- 用于加载一系列数据的协议,例如项目 478712 到 478901,而不是整个数据
- 用于接收有关更改数据的更新的协议
- 一个缓存类,通过它们在服务器上的已知索引存储项目
- 属于与服务器通信的缓存的线程。这是写入集合本身的唯一线程
- 属于该缓存的线程,在检索数据时处理回调
- UI 组件实现的接口,以允许它们在加载数据时接收数据
乍一看,这个缓存的骨骼可能看起来像这样:
class ServerCacheViewThingy {
private static final int ACCEPTABLE_SIZE = 500;
private int viewStart, viewLength;
final Map<Integer, Record> items
= new HashMap<Integer, Record>(1000);
final ConcurrentLinkedQueue<Callback> callbackQueue
= new ConcurrentLinkedQueue<Callback>();
public void getRecords (int start, int length, ViewReciever reciever) {
// remember the current view, to prevent records within
// this view from being accidentally pruned.
viewStart = start;
viewLenght = length;
// if the selected area is not already loaded, send a request
// to load that area
if (!rangeLoaded(start, length))
addLoadRequest(start, length);
// add the reciever to the queue, so it will be processed
// when the data has arrived
if (reciever != null)
callbackQueue.add(new Callback(start, length, reciever));
}
class Callback {
int start;
int length;
ViewReciever reciever;
...
}
class EditorThread extends Thread {
private void prune () {
if (items.size() <= ACCEPTABLE_SIZE)
return;
for (Map.Entry<Integer, Record> entry : items.entrySet()) {
int position = entry.key();
// if the position is outside the current view,
// remove that item from the cache
...
}
}
private void markDirty (int from) { ... }
....
}
class CallbackThread extends Thread {
public void notifyCallback (Callback callback);
private void processCallback (Callback) {
readRecords
}
}
}
interface ViewReciever {
void recieveData (int viewStart, Record[] records);
void recieveTimeout ();
}
显然,您必须自己填写很多细节。