首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何制作线程安全集合的防御性副本

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

Stack Overflow用户
提问于 2016-02-12 05:09:50
回答 3查看 1K关注 0票数 1

下面的类有一个私有的线程安全集合,声明如下:

代码语言:javascript
复制
final private ConcurrentHashMap<Book,BookLog> booklogMap;

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

代码语言:javascript
复制
fDateOfDiscovery = new Date(aDateOfDiscovery.getTime()) 

在像这样的getter中:

代码语言:javascript
复制
 public Date getDateOfDiscovery() {
    return new Date(fDateOfDiscovery.getTime());
  }

那么,如何在构造函数中正确地创建ConcurrentHashMap的防御性副本呢?我不能使用Collections.modifiableMap(),因为它会遇到cast问题。

EN

回答 3

Stack Overflow用户

发布于 2016-02-12 05:20:02

您可能想要也可能不想使用Collections.unmodifiableMap(),因为它返回一个Map,而不是一个ConcurrentHashMap,而且您可能不能强制转换它(尽管我还没有尝试过)。这可能不是一件坏事,因为其他类可能不需要知道这个特定的Map是并发散列的变体。

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

代码语言:javascript
复制
new ConcurrentHashMap<Book, Booklog>(booklogMap);

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

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

票数 1
EN

Stack Overflow用户

发布于 2016-02-12 06:15:54

使用示例编辑

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

代码语言:javascript
复制
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"));
    }
}

最初用于历史目的:几件事,

首先,如果您将变量/字段/常量都定义为接口而不是特定的具体类,这会更好。因此,您应该将您的映射定义为:

代码语言:javascript
复制
//conventions usually prefer private final, as opposed to final private
private final Map<Book,BookLog> booklogMap; 

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

因此,举个例子:

代码语言:javascript
复制
//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所有的书和BookLog。

票数 0
EN

Stack Overflow用户

发布于 2016-02-12 09:40:10

你在评论中说过Book和BookLog是不可变的,所以你不需要对它们进行防御性的复制。但是你也在评论中说new ConcurrentHashMap<Book, Booklog>(booklogMap);不做拷贝,这没有任何意义,因为它做了:

代码语言:javascript
复制
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}
}

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35350075

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档