如何制作线程安全集合的防御副本

时间:2016-02-11 21:09:50

标签: java multithreading

我有以下类,其中声明了一个私有线程安全集合:

final private ConcurrentHashMap<Book,BookLog> booklogMap;

根据这个page,因为日期是可变的并且它在不可变类中使用,所以必须制作防御性副本以避免在创建后更改对象。他们在构造函数中制作一个防御性副本,如下所示:

fDateOfDiscovery = new Date(aDateOfDiscovery.getTime()) 

在这样的吸气器中:

 public Date getDateOfDiscovery() {
    return new Date(fDateOfDiscovery.getTime());
  }

如何在构造函数中正确创建ConcurrentHashMap的防御副本?我不能使用Collections.modifiableMap(),因为它会遇到强制转换问题。

3 个答案:

答案 0 :(得分:1)

您可能希望也可能不希望使用Collections.unmodifiableMap(),因为它会返回Map,而不是ConcurrentHashMap,而您可能无法投放它(尽管我没有&#39} ;尝试过)。这可能不是一件坏事,因为其他类可能不需要知道这个特殊的Map属于Concurrent Hash变种。

@resueman在评论中建议你可以使用

new ConcurrentHashMap<Book, Booklog>(booklogMap);

这会创建ConcurrentHashMap本身的(防御性)副本,但不会创建Map内容的副本。这可能没问题,也可能没有,这取决于Map的用户想要用它做什么。

并且,如果您决定需要复制内容,则必须决定是否复制这些副本包含的字段值。等等,直到你停下来。

答案 1 :(得分:0)

使用示例编辑

这将复制输入映射,在请求时安全地提供地图的不可变副本,并在需要时安全地返回地图元素:

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


public class CopyMap {

    //just an example Long and String are both immutable
    //replace Long for Book (Book has to implement hashcode and equals.
    // replace String for Booklog
    private final Map<Long, String> map;

    public CopyMap(Map<Long,String> map) {
        this.map = new ConcurrentHashMap<>(map);
    }

    // get a non editable copy of the whole map
    // threadsafe because this.map is concurrentHashMap
    // no synchronization needed
    public Map<Long,String> getMap() {
        return Collections.unmodifiableMap(this.map);
    }

    //get only one of the elements
    //thread safe because implementation is ConcurrentHashMap
    //no synchronization needed
    public String get(Long key) {
        return map.get(key);
    }

    //just to try it, with -ea enabled
    public static void main(String... args) {
        final Map<Long,String> map1 = new ConcurrentHashMap<>();
        map1.put(1l,"1");
        map1.put(2l,"2");
        map1.put(3l,"3");

        final CopyMap copyMap = new CopyMap(map1);

        assert(copyMap.getMap().equals(map1));
        assert(copyMap.getMap()!=map1);

        assert(copyMap.get(1l).equals("1"));
        assert(copyMap.get(2l).equals("2"));
        assert(copyMap.get(3l).equals("3"));
    }
}

原创用于历史目的: 一些事情,

首先,如果将变量/ fields / constants全部定义为接口而不是特定的具体类,则更好。因此,您应该将地图定义为:

//conventions usually prefer private final, as opposed to final private
private final Map<Book,BookLog> booklogMap; 

然后,其次,您确实可以使用Collections.unmodifiableMap()

所以一个例子是:

//package and imports here
public class MyClass {

  private final Map<Book,BookLog> booklogMap;

  public MyClass(Map<Book, BookLog> booklogMap) {
    this.booklogMap = Collections.unmodifiableMap(booklogMap);
  }
}

第三,为了让你拥有真正的不变性,你需要使整个对象层次结构不可变。因此,您的类BookBookLog也必须是不可变的。

否则,您需要在构造函数中逐个deep copy所有Book(s)和BookLog(s)。

答案 2 :(得分:0)

你在评论中已经说过Book和BookLog是不可变的,所以你不需要制作它们的防御性副本。但你也在评论中说public static void main(String[] args) { Map<String, Integer> map = new ConcurrentHashMap<>(); map.put("apples", 3); System.out.println(map); // prints {apples=3} Map<String, Integer> map2 = new ConcurrentHashMap<>(map); map2.put("oranges", 1); System.out.println(map2); // prints {oranges=1, apples=3} System.out.println(map); //prints {apples=3} } 没有复制,这没有任何意义,因为它确实:

{{1}}

如果这些答案对您没有帮助,那么您真的需要澄清您的问题。哪些对象是不可变的?你试图阻止什么突变?你能举个例子吗?