我一直在网上搜索这个,但一直找不到任何接近此文章的文章,我对此感到非常惊讶。也许智慧隐藏在我尚未找到的地方。
假设我有一个包含10个不同类型成员的类(为了简单起见,假设它们混合了int和Strings),并且每个类都有自己的访问器方法。现在,我想让这个类线程安全。但是,其中一些数据成员不一定互相交互。例如,下面的课程Person
包含age
和name
以及其他属性。
public class Person {
private volatile int age;
private String name;
private volatile long blabla;
// ... and so on
public synchronized int getAge() {
return age;
}
public synchronized void setAge(int age) {
this.age = age;
}
// .. and so on for each data member
}
一个线程可能只需要读/写age
,而其他线程只需要修改name
。显然,将synchronized
添加到每个访问器方法是一个坏主意,因为它锁定了对象的整个实例。正在调用getAge()
的线程必须等待另一个正在调用getName()
的线程,即使age
和name
是两个单独的字段。
因此,一个明显的解决方案是为每个字段创建一个锁(或将volatile
添加到基本类型)。然而,这似乎是一种矫枉过正。如果我有10个数据成员,我还需要10个锁吗?我想知道是否有另一种方法可以在没有过多锁定的情况下实现这一点。
答案 0 :(得分:4)
首先,如果您正在谈论基元(或String
等不可变对象),那么您应该只需要标记每个字段volatile
。如果您所做的只是获取和设置字段值,则无需锁定。
但是,如果你的get / set方法执行多个操作并且需要synchronized
块,那么每个字段有synchronized
个块似乎对我来说过早优化。我认为像synchronized
这样的小对象上的Person
方法是完成此任务的完美方法。除非你有真正的理由(即分析器输出),否则我不会试图让它更复杂。 当然每个字段的锁定在任何情况下都是过度的。
如果该方法需要很长时间,那将会有所不同。然后你不想要锁定整个对象并阻止其他访问者。那么现在是拥有多个锁的好时机 - 每个锁用于单独计算。但是如果你的对象真的只是想保护get / set,那么synchronized
方法就可以了。
其他几条评论:
volatile
字段,则不需要任何synchronized
块。synchronized
方法,则无需创建字段volatile
。name
字段未被写入,则可能应标记为final
。答案 1 :(得分:4)
如果您担心同步基元类型,这是AtomicInteger等的一个很好的用例......它们非常快并且确保线程安全。欲了解更多信息:
http://docs.oracle.com/javase/tutorial/essential/concurrency/atomicvars.html