Java一致性同步
我们在Spring服务中,在多线程环境中面临以下问题:
- 三个列表被自由且独立地偶尔(每5分钟)读取
- 一次,它们都被更新为新值。列表之间存在一些依赖性,例如,当第二个列表正在更新且第一个列表已具有新值时,不应读取第三个列表;这将打破三个列表的一致性。
我最初的想法是创建一个具有三个列表作为属性的容器对象。
然后,首先在该对象上进行同步,然后在三个列表中逐一进行同步。
有些代码值得几千字......所以这是一个草案
private class Sync {
final List<Something> a = Collections.synchronizedList(new ArrayList<Something>());
final List<Something> b = Collections.synchronizedList(new ArrayList<Something>());
final List<Something> c = Collections.synchronizedList(new ArrayList<Something>());
}
private Sync _sync = new Sync();
...
void updateRunOnceEveryFiveMinutes() {
final List<Something> newa = new ArrayList<Something>();
final List<Something> newb = new ArrayList<Something>();
final List<Something> newc = new ArrayList<Something>();
...building newa, newb and newc...
synchronized(_sync) {
synchronized(_sync.a) {
_synch.a.clear();
_synch.a.addAll(newa);
}
synchronized(_sync.b) { ...same with newb... }
synchronized(_sync.c) { ...same with newc... }
}
// Next is accessed by clients
public List<Something> getListA() {
return _sync.a;
}
public List<Something> getListB() { ...same with b... }
public List<Something> getListC() { ...same with c... }
问题是,
- 这个草案安全吗(没有死锁,数据一致性)?
- 针对该具体问题您有更好的实施建议吗?
更新
更改了 _sync 同步和 newa... 构建的顺序。
谢谢
We are facing the following problem in a Spring service, in a multi-threaded environment:
- three lists are freely and independently accessed for Read
- once in a while (every 5 minutes), they are all updated to new values. There are some dependencies between the lists, making that, for instance, the third one should not be read while the second one is being updated and the first one already has new values ; that would break the three lists consistency.
My initial idea is to make a container object having the three lists as properties.
Then the synchronization would be first on that object, then one by one on each of the three lists.
Some code is worth a thousands words... so here is a draft
private class Sync {
final List<Something> a = Collections.synchronizedList(new ArrayList<Something>());
final List<Something> b = Collections.synchronizedList(new ArrayList<Something>());
final List<Something> c = Collections.synchronizedList(new ArrayList<Something>());
}
private Sync _sync = new Sync();
...
void updateRunOnceEveryFiveMinutes() {
final List<Something> newa = new ArrayList<Something>();
final List<Something> newb = new ArrayList<Something>();
final List<Something> newc = new ArrayList<Something>();
...building newa, newb and newc...
synchronized(_sync) {
synchronized(_sync.a) {
_synch.a.clear();
_synch.a.addAll(newa);
}
synchronized(_sync.b) { ...same with newb... }
synchronized(_sync.c) { ...same with newc... }
}
// Next is accessed by clients
public List<Something> getListA() {
return _sync.a;
}
public List<Something> getListB() { ...same with b... }
public List<Something> getListC() { ...same with c... }
The question would be,
- is this draft safe (no deadlock, data consistency)?
- would you have a better implementation suggestion for that specific problem?
update
Changed the order of _sync synchronization and newa... building.
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
你最好的选择是公开一个“当前状态”对象并使用易失性,放弃所有同步代码。 (另外,为了安全起见,您可能希望使列表成为不可修改的包装器)。
your best option is to expose a "current state" object and use volatile, ditching all the synchronized code. (also, you may want to make the lists unmodifiable wrappers just to be safe).
构建一个新列表只是将其复制到另一个(最终)列表中有点浪费。
考虑使用不可变列表并将新列表分配给
_sync.a
、_sync.b
和_sync.c
(已删除final构建列表后(在 updateRunOnceEveryFiveMinutes 中)。
此外,您还在 4 个不同的对象上进行同步。对于每个方法调用,仅同步一个对象(
_sync
或可能是一个专用锁对象)会更简单。如果 getListA 等返回的列表是不可变的,您也不需要调用 Collections.synchronizedList。
It's a bit wasteful to build a new list only to copy it into another (final) list.
Consider using immutable lists and assigning the new list to
_sync.a
,_sync.b
and_sync.c
(having removed thefinal
modifier from them) after building the list (inupdateRunOnceEveryFiveMinutes
.)Also you are synchronizing on 4 different objects. It would be simpler to synchronize on just one object (
_sync
or possibly a dedicated lock object) for each method call.If the lists returned by
getListA
etc. are immutable you do also not need the calls toCollections.synchronizedList
.草案没有实现你的目标。这里没有任何东西可以阻止在计算新的 A 或 B 时访问旧的 C。实现这一点的最简单方法如下:
围绕所有访问,无论是读还是写。我不会执行clear()和addAll(),只需将变量值更改为newA/newB/newC,尽管这需要对上述内容进行一些调整。
The draft doesn't accomplish your objective. There is nothing here that prevents access to the old C while the new A or B are being computed. The simplest way to accomplish that is like so:
around all accesses whether read or write. And I wouldn't do clear() and addAll(), just change the variable value to newA/newB/newC, although that would require some adjustment to the above.