什么案例需要Java中的同步方法访问?

在什么情况下需要同步访问实例成员? 我理解,对类的静态成员的访问总是需要同步 – 因为它们在类的所有对象实例中共享。

我的问题是,如果我不同步实例成员,我什么时候会不正确?

例如,如果我的class级是

public class MyClass { private int instanceVar = 0; public setInstanceVar() { instanceVar++; } public getInstanceVar() { return instanceVar; } } 

在什么情况下(使用类MyClass )我需要有方法: public synchronized setInstanceVar()public synchronized getInstanceVar()

提前感谢您的回答。

这取决于您是否希望您的类是线程安全的。 大多数类不应该是线程安全的(为简单起见),在这种情况下,您不需要同步。 如果您需要它是线程安全的,您应该同步访问使变量volatile。 (它避免了其他线程获得“陈旧”数据。)

synchronized修饰符确实是一个主意,应该不惜一切代价避免。 我认为值得称道的是,Sun试图让锁定更容易实现,但synchronized只会造成比它值得更多的麻烦。

问题在于,一个synchronized方法实际上只是获取锁定的语法糖,并在方法的持续时间内持有它。 因此, public synchronized void setInstanceVar()将等效于以下内容:

 public void setInstanceVar() { synchronized(this) { instanceVar++; } } 

这有两个原因:

  • 同一类中的所有synchronized方法使用完全相同的锁,这会降低吞吐量
  • 任何人都可以访问锁,包括其他类的成员。

没有什么可以阻止我在另一个类中做这样的事情:

 MyClass c = new MyClass(); synchronized(c) { ... } 

在该synchronized块中,我持有MyClass所有synchronized方法所需的锁。 这进一步降低了吞吐量并大大增加了死锁的可能性。

更好的方法是拥有一个专用的lock对象并直接使用synchronized(...)块:

 public class MyClass { private int instanceVar; private final Object lock = new Object(); // must be final! public void setInstanceVar() { synchronized(lock) { instanceVar++; } } } 

或者,您可以使用java.util.concurrent.Lock接口和java.util.concurrent.locks.ReentrantLock实现来实现基本相同的结果(事实上,它在Java 6上是相同的)。

如果你想让这个类线程安全,我会将instanceVar声明为volatile ,以确保你始终从内存中获得最新的值,并且我也会使setInstanceVar() synchronized因为在JVM中,增量不是primefaces操作。

 private volatile int instanceVar =0; public synchronized setInstanceVar() { instanceVar++; } 

。 粗略地说,答案是“它取决于”。 在此处同步setter和getter只能保证多个线程无法在每个其他增量操作之间读取变量:

  synchronized increment() { i++ } synchronized get() { return i; } 

但是这在这里真的不会起作用,因为为了确保你的调用者线程获得相同的增量值,你必须保证你primefaces上递增然后检索,你在这里没有做 – 即你我必须做点什么

  synchronized int { increment return get() } 

基本上,同步对于定义哪些操作需要保证运行线程安全是有用的(换句话说,您不能创建一个单独的线程破坏您的操作并使您的类行为不合理或破坏您期望的数据状态的情况成为)。 这实际上是一个比这里可以解决的更大的话题。

本书Java Concurrency in Practice非常出色,当然比我更可靠。

简单地说,当你有多个线程访问相同实例的相同方法时,你会使用synchronized,这将改变对象/或应用程序的状态。

它是一种防止线程之间竞争条件的简单方法,实际上,当您计划让并发线程访问同一个实例(例如全局对象)时,您应该只使用它。

现在,当您使用并发线程读取对象实例的状态时,您可能需要查看java.util.concurrent.locks.ReentrantReadWriteLock – 理论上它允许一次读取多个线程,但仅限于允许一个线程写入。 因此,在每个人似乎都在给予的getter和设置方法示例中,您可以执行以下操作:

 public class MyClass{ private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); private int myValue = 0; public void setValue(){ rwl.writeLock().lock(); myValue++; rwl.writeLock().unlock(); } public int getValue(){ rwl.readLock.lock(); int result = myValue; rwl.readLock.unlock(); return result; } } 

在Java中,对int的操作是primefaces的,所以没有,在这种情况下,如果你所做的只是1次写入和1次读取,则不需要同步。

如果这些是long或double,你需要同步,因为有可能更新long / double的一部分,然后读取另一个线程,然后最后更新long / double的另一部分。