如何确保多个线程可以安全地访问类字段?
当多个线程通过 getter 方法访问类字段时,如何维护线程安全? 同步关键字足够吗?
这安全吗:
public class SomeClass {
private int val;
public synchronized int getVal() {
return val;
}
private void setVal(int val) {
this.val = val;
}
}
还是设置器会带来更多的并发症?
When a class field is accessed via a getter method by multiple threads, how do you maintain thread safety? Is the synchronized keyword sufficient?
Is this safe:
public class SomeClass {
private int val;
public synchronized int getVal() {
return val;
}
private void setVal(int val) {
this.val = val;
}
}
or does the setter introduce further complications?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
如果您也在此处的 setter 上使用“同步”,则此代码是线程安全的。 然而,它可能不够细化; 如果您有 20 个 getter 和 setter,并且它们全部同步,则可能会造成同步瓶颈。
在这个特定的实例中,使用单个 int 变量,然后消除“同步”并将 int 字段标记为“易失性”也将确保可见性(每个线程在调用 getter 时将看到“val”的最新值),但它可能不会足够同步以满足您的需求。 例如,
当且仅当 val 已经为 1 时,期望将 val 设置为 2 是不正确的。 为此,您需要一个外部锁或某种原子比较和设置方法。
我强烈建议您阅读 Brian Goetz 等人编写的 Java Concurrency In Practice,它对 Java 的并发结构有最好的介绍。
If you use 'synchronized' on the setter here too, this code is threadsafe. However it may not be sufficiently granular; if you have 20 getters and setters and they're all synchronized, you may be creating a synchronization bottleneck.
In this specific instance, with a single int variable, then eliminating the 'synchronized' and marking the int field 'volatile' will also ensure visibility (each thread will see the latest value of 'val' when calling the getter) but it may not be synchronized enough for your needs. For example, expecting
to set val to 2 if and only if it's already 1 is incorrect. For this you need an external lock, or some atomic compare-and-set method.
I strongly suggest you read Java Concurrency In Practice by Brian Goetz et al, it has the best coverage of Java's concurrency constructs.
除了 Cowan 的评论< /a>,您可以执行以下操作来进行比较和存储:
这有效,因为通过同步方法定义的锁与对象的锁隐式相同(请参阅 Java 语言规范)。
In addition to Cowan's comment, you could do the following for a compare and store:
This works because the lock defined via a synchronized method is implicitly the same as the object's lock (see java language spec).
根据我的理解,您应该在 getter 和 setter 方法上使用同步,这就足够了。
编辑:这是一个链接,指向一些更多信息关于同步和什么不是。
From my understanding you should use synchronized on both the getter and the setter methods, and that is sufficient.
Edit: Here is a link to some more information on synchronization and what not.
如果您的类仅包含一个变量,那么实现线程安全的另一种方法是使用现有的 AtomicInteger 对象。
但是,如果您添加其他变量以使它们相互依赖(一个变量的状态取决于另一个变量的状态),则 AtomicInteger 将不起作用。
赞同阅读《Java 并发实践》的建议。
If your class contains just one variable, then another way of achieving thread-safety is to use the existing AtomicInteger object.
However, if you add additional variables such that they are dependent (state of one variable depends upon the state of another), then AtomicInteger won't work.
Echoing the suggestion to read "Java Concurrency in Practice".
对于简单的对象来说这可能就足够了。 在大多数情况下,您应该避免使用synchronized 关键字,因为您可能会遇到同步死锁。
示例:
确保只有一个线程读取或写入本地实例成员。
阅读《Java并发编程(tm):设计原则和模式(Java (Addison-Wesley))》一书,也许http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html 也很有帮助...
For simple objects this may suffice. In most cases you should avoid the synchronized keyword because you may run into a synchronization deadlock.
Example:
Assures that only one thread reads or writes to the local instance member.
Read the book "Concurrent Programming in Java(tm): Design Principles and Patterns (Java (Addison-Wesley))", maybe http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html is also helpful...
同步的存在是为了防止线程干扰和内存一致性错误。 通过在 getVal() 上进行同步,代码可以保证 SomeClass 上的其他同步方法不会同时执行。 由于没有其他同步方法,因此它没有提供太多价值。 另请注意,对原语的读取和写入具有原子访问权限。 这意味着通过仔细编程,不需要同步对该字段的访问。
请阅读同步。
不太清楚为什么这个值下降到-3。 我只是总结 Sun 的同步教程的内容(以及我自己的经验)。
Synchronization exists to protect against thread interference and memory consistency errors. By synchronizing on the getVal(), the code is guaranteeing that other synchronized methods on SomeClass do not also execute at the same time. Since there are no other synchronized methods, it isn't providing much value. Also note that reads and writes on primitives have atomic access. That means with careful programming, one doesn't need to synchronize the access to the field.
Read Sychronization.
Not really sure why this was dropped to -3. I'm simply summarizing what the Synchronization tutorial from Sun says (as well as my own experience).