【发布时间】:2015-01-08 13:21:35
【问题描述】:
我在 Web 服务中使用了一个静态地图对象,它将在多线程环境中使用。
private static Map<String, String> map;
地图以键值对的形式保存数据库中的记录。因为,数据库很大,为每个请求执行读取操作将是一件昂贵的事情。我在服务启动期间读取了地图对象中的完整数据库,并且由于所有对象都应该共享相同的记录,因此我将其声明为静态。
static
{
map = loadDatabase();
}
private Map<String,String> loadDatabase()
{
//database operations
Map<String,String> sortedMap = new TreeMap<String,String>(new LengthBasedComparator());
//put all records in a map in the length sorted manner
return sortedmap
}
我使用了TreeMap,因为我希望它根据键的长度进行排序。这是使用LenghBasedComparator 类完成的
如果它只是读取操作,它会工作得很好。
public String getStringMatch(String key)
{
// additional processing
map.get(key);
// more processing
}
现在,更新内存数据库即地图的新要求出现了。
public String updateInMemoryDatabase(String key, String value)
{
//....
synchronized(map)
{
map.put(key,value);
}
return SUCCESS;
//......
}
我已将put 操作放在地图上的synchronized 块中。
这是同步访问地图的正确方法吗?
或者,
- 使地图易变是否达到目的?
- 由于我需要排序方式的地图,我不能使用 ConcurrentHashMap。因为,它可能会改变排序。有什么使用方法吗?
请指点一下这方面。
谢谢
无所谓
【问题讨论】:
-
“数据库很大...我已经阅读了地图对象中的完整数据库”我知道“巨大”是一个主观术语,但如果它适合内存,你真的可以称之为巨大吗?
-
同步 put() 是不够的。您还必须同步所有 get()。否则,一个线程可能会在另一个线程处于同步 put() 中间时执行不同步的 get(),并且第一个线程可能会看到 Map 处于无效状态,因此会得到错误的结果甚至使程序崩溃.
-
不,
volatile无济于事。它不会阻止两个线程同时访问映射,在这种情况下,它可能根本不做任何事情,因为在第一次赋值之后没有线程写入map变量。注意:不存在 volatile object 这样的东西。如果你写volatile Map<...> map;,那是变量的一个属性。它不会改变变量所引用的 Map 对象的任何内容。 -
在 TreeMap 中使用自定义比较器时要小心。如果您的比较器真的只比较长度,那么字符串的实际值无关紧要。在这一点上,你还不如拥有一个由 Integer(字符串长度)键入的映射。
-
@NishitJain 如果比较器说 2 个值相等(即长度相同),则映射会将 2 个键视为相等。如果这不是所需的行为,您需要通过提供另一个比较器来处理长度相同的情况。您可以恢复到
String的自然比较作为辅助排序。在 Java 8 中:Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder())
标签: java multithreading web-services synchronization