为什么Properties.list在打印前复制表?

时间:2017-01-24 14:24:57

标签: java collections

这里是JDK Properties.list的实现:

public void list(PrintWriter out) {
    out.println("-- listing properties --");
    Hashtable<String,Object> h = new Hashtable<>();
    enumerate(h);
    for (Enumeration<String> e = h.keys() ; e.hasMoreElements() ;) {
        String key = e.nextElement();
        String val = (String)h.get(key);
        if (val.length() > 40) {
            val = val.substring(0, 37) + "...";
        }
        out.println(key + "=" + val);
    }
}

行是什么

Hashtable<String,Object> h = new Hashtable<>();
enumerate(h);

有关?它不能只使用原始表而不是复制并从中读取数据吗?

3 个答案:

答案 0 :(得分:2)

这是允许线程安全迭代的常用习惯用法(即,如果另一个线程在循环期间修改哈希表,则可能出现ConcurrentModificationException。)

答案 1 :(得分:2)

list()方法未同步。
因此,在Properties调用期间list()实例上的任何调用(甚至那些已同步)都可能会破坏对象的状态并导致异常或逻辑损坏。

假设使用中间的Properties实例,我们直接在Properties实例上迭代,它将不是线程安全的

假设我们有两个线程:调用此list()方法的线程A和在同一remove()实例上调用Properties的线程B.

list()方法中的此代码具有多种竞争条件。

为了说明,我们可以使用list()方法的这个片段:

   String key = e.nextElement();
   String val = (String)h.get(key);
   if (val.length() > 40) {

假设在此指令后线程A暂停:

 String key = e.nextElement();  

线程B恢复并使用作为参数调用与线程A检索到的密钥相关联的对象instance.remove()

然后当线程A恢复时,它转到下一条指令:

 String val = (String)h.get(key);

该值不再存在。

下一条指令引发异常,因为val为null

    if (val.length() > 40) {

答案 2 :(得分:1)

这是多线程应用程序的优化。

Properties对象意味着线程安全,因此他们必须使方法同步(这是一个坏主意,因为写入PrintStream可能会阻塞很长时间,持有锁定),或者他们可以快速将内容复制到同步块中的新哈希表(enumerate已同步),然后打印副本的项目,而不需要锁定。