10

当多个线程通过getter方法访问一个类字段时,如何维护线程安全?同步关键字是否足够?

这安全吗:

public class SomeClass {
    private int val;

    public synchronized int getVal() {
        return val;
    }

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

还是二传手会引入更多的复杂性?

4

6 回答 6

17

如果您在此处的设置器上也使用“同步”,则此代码是线程安全的。然而,它可能不够精细;如果您有 20 个 getter 和 setter,并且它们都是同步的,那么您可能会造成同步瓶颈。

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

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

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

我强烈建议您阅读Brian Goetz等人的Java Concurrency In Practice,它对 Java 的并发结构有最好的覆盖。

于 2008-09-23T00:22:28.010 回答
3

除了Cowan 的评论之外,您还可以执行以下操作进行比较和存储:

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

这是有效的,因为通过同步方法定义的锁与对象的锁隐式相同(参见 java 语言规范)。

于 2008-09-23T00:39:05.087 回答
2

据我了解,您应该在 getter 和 setter 方法上使用同步,这就足够了。

编辑:这是一个链接,指向有关同步的更多信息以及其他信息。

于 2008-09-23T00:19:12.013 回答
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”的建议。

于 2009-08-07T15:56:49.327 回答
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也很有帮助...

于 2008-09-23T10:37:47.253 回答
-3

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

阅读同步

不太确定为什么将其降至-3。我只是在总结 Sun 的同步教程所说的(以及我自己的经验)。

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

于 2008-09-23T00:33:20.577 回答