您如何确保多个线程可以安全地访问类字段?

时间:2008-09-23 00:17:29

标签: java multithreading synchronized

当多个线程通过getter方法访问类字段时,如何保持线程安全? synchronized参数是否足够?

这是安全的吗?

public class SomeClass {
    private int val;

    public synchronized int getVal() {
        return val;
    }

    private void setVal(int val) {
        this.val = val;
    }
}

或者是否会引入进一步的并发症?

6 个答案:

答案 0 :(得分:17)

如果您也在此处使用setter上的'synchronized',则此代码是线程安全的。但是它可能不够细小;如果你有20个getter和setter并且它们都是同步的,你可能会创建一个同步瓶颈。

在这个特定的实例中,使用单个int变量,然后消除'synchronized'并将int字段标记为'volatile'也将确保可见性(每个线程在调用getter时将看到'val'的最新值)但是它可能无法满足您的需求。例如,期待

 int old = someThing.getVal();
 if (old == 1) {
    someThing.setVal(2);
 }

将val设置为2当且仅当它已经为1时才是错误的。为此,您需要一个外部锁或一些原子比较和设置方法。

我强烈建议您阅读Brian Goetz 中的 Java Concurrency In Practice ,它具有Java的并发结构的最佳覆盖率。

答案 1 :(得分:3)

除了Cowan's comment之外,您还可以执行以下比较和存储:

synchronized(someThing) {
    int old = someThing.getVal();
    if (old == 1) {
        someThing.setVal(2);
    }
}

这是有效的,因为通过synchronized方法定义的锁与对象的锁(see java language spec)隐式相同。

答案 2 :(得分:2)

根据我的理解,你应该在getter和setter方法上使用synchronized,这就足够了。

编辑:这是link以获取有关同步的更多信息,而不是。

答案 3 :(得分:1)

如果您的类只包含一个变量,那么实现线程安全的另一种方法是使用现有的AtomicInteger对象。

public class ThreadSafeSomeClass {

    private final AtomicInteger value = new AtomicInteger(0);

    public void setValue(int x){
         value.set(x);
    }

    public int getValue(){
         return value.get();
    }

}

但是,如果添加其他变量以使它们相关(一个变量的状态取决于另一个变量的状态),则AtomicInteger将不起作用。

回应建议,阅读“Java Concurrency in Practice”。

答案 4 :(得分:0)

对于简单的物体,这可能就足够了。在大多数情况下,您应该避免使用synchronized关键字,因为您可能会遇到同步死锁。

示例:

public class SomeClass {

  private Object mutex = new Object();
  private int    val   = -1; // TODO: Adjust initialization to a reasonable start
                             //       value
  public int getVal() {
    synchronized ( mutex ) {
      return val;
    }
  }

  private void setVal( int val ) {
    synchronized ( mutex ) {
      this.val = val;
    }
  }
}

确保只有一个线程读取或写入本地实例成员。

阅读“Java中的并行编程(tm):设计原则和模式(Java(Addison-Wesley))”一书,也许http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html也很有用......

答案 5 :(得分:-3)

存在同步以防止线程干扰和内存一致性错误。通过在getVal()上进行同步,代码可以保证SomeClass上的其他同步方法也不会同时执行。由于没有其他同步方法,因此没有提供太多价值。另请注意,对基元的读取和写入具有原子访问权限。这意味着通过仔细编程,不需要同步对字段的访问。

阅读Sychronization

不确定为什么会降到-3。我只是总结一下Sun的同步教程(以及我自己的经验)。

  

使用简单的原子变量访问是   比访问这些更有效   变量通过同步代码,   但需要更多的照顾   程序员避免内存一致性   错误。是否额外的努力   值得取决于大小和   应用程序的复杂性。