Java中对象的线程安全复制

时间:2012-04-17 12:42:11

标签: java multithreading thread-safety synchronized

我有一个类似于以下类的静态数组:

public class Entry {
    private String sharedvariable1= "";
    private String sharedvariable2= "";
    private int sharedvariable3= -1;

    private int mutablevariable1 = -1
    private int mutablevariable2 = -2;

    public Entry (String sharedvariable1, 
                  String sharedvariable2, 
                  int sharedvariable3) {
        this.sharedvariable1 = sharedvariable1;
        this.sharedvariable2 = sharedvariable2;
        this.sharedvariable3 = sharedvariable 3;
    }

    public Entry (Entry entry) {  //copy constructor. 
        this (entry.getSharedvariable1, 
              entry.getSharedvariable2, 
              entry.getSharedvaraible3);
    }
....
/* other methods including getters and setters*/
}

在我的程序中的某个时刻,我访问该对象的一个​​实例,并使用上面的复制构造函数复制它。然后我改变上面两个可变变量的值。该程序在多线程环境中运行。 请注意。所有变量都在线程之前设置其初始值。只有在程序被线程化后才能复制变量。我相信它是线程安全的,因为我只是读取静态对象,而不是写入它(即使是共享变量3,虽然只读取了int和mutable),我只是对静态对象的副本进行了更改(以及正在线程内进行复制)。但是,我想确认我的想法是正确的。

有人可以评估我在做什么吗?

5 个答案:

答案 0 :(得分:3)

它不是线程安全的。你需要包装修改共享变量的任何东西:

synchronized (this) {
    this.sharedvariable1 = newValue;
}

对于setter,您可以这样做:

public synchronized void setSharedvariable1(String sharedvariable1) {
    this.sharedvariable1 = sharedvariable1;
}

然后在你的复制构造函数中,你会做同样的事情:

public Entry (Entry entry) {
    this();
    synchronized(entry) {
        this.setSharedvariable1(entry.getSharedvariable1());
        this.setSharedvariable2(entry.getSharedvariable2());
        this.setSharedvariable3(entry.getSharedvariable3());
    }
}

这确保了如果对实例进行了修改,则复制操作将一直等到修改完成。

答案 1 :(得分:0)

它不是线程安全的,您应该在复制构造函数中进行同步。您正在从复制构造函数中的原始对象中读取三个变量中的每一个。这些操作不是原子的。因此,当您正在读取第一个值时,第三个值会被另一个线程更改。在这种情况下,您有一个处于不一致状态的“复制”对象。

答案 2 :(得分:0)

这不是线程安全的。我的意思是,对于使用相同Entry实例的多个线程,它不会保证线程安全。

我在这里看到的问题如下:

  1. Thread 1开始构建Entry实例。它不会使该实例对其他线程的访问保持隐藏。
  2. Thread 2使用其复制构造函数访问该实例,同时它仍在构建中。
  3. 考虑Entry字段private int sharedvariable3= -1;的初始值,结果可能是Thread 2创建的新“已复制”实例将设置其sharedvariable3字段到0(java中int类字段的默认值)。

    这就是问题所在。

    如果它困扰你,你必须进行synchronize读/写操作,或者处理Entry实例发布。这意味着,不允许其他线程访问正在构建中的Entry实例。

答案 3 :(得分:0)

我真的不明白,为什么你认为私有实例变量是共享的。通常共享字段是静态的而不是私有的 - 我建议您不要共享私有实例变量。对于线程安全性,您应该同步变量值的操作。

您可以使用synchronized关键字,但选择正确的监视器对象(我认为条目本身应该这样做)。另一种方法是使用java.util.concurrent中的一些锁实现。通常,锁提供更高的吞吐量和更好的粒度(例如,多个并行读取,但在任何给定时间只能写入一次)。

你必须考虑的另一件事是所谓的记忆障碍。看看这篇有趣的文章http://java.dzone.com/articles/java-memory-model-programer%E2%80%99s

您可以使用volatile关键字在语义之前强制执行此操作。显式同步(锁定或同步代码)也跨越内存屏障并在语义之前强制执行。

最后一条建议:你应该不惜一切代价避免共享的可变状态。同步是一种痛苦(性能和维护方面)。由于不正确的同步导致的错误非常难以检测。设计不可变性或孤立的可变性(例如演员)更好。

答案 4 :(得分:0)

答案是在规定的条件下它是线程安全的,因为我只是从静态状态的变量中读取而只是更改副本。