哪些情况需要在 Java 中进行同步方法访问?

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

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

例如,如果我的类是

public class MyClass {
    private int instanceVar = 0;

    public setInstanceVar()
    {
        instanceVar++;
    }

    public getInstanceVar()
    {
        return instanceVar;
    }
}

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

提前感谢您的回答。


答案 1

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

问题在于,方法实际上只是语法糖,用于在方法期间打开锁定并保持它。因此,将等效于类似下面的内容:synchronizedthispublic synchronized void setInstanceVar()

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

这很糟糕,原因有两个:

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

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

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

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

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

public class MyClass {
    private int instanceVar;
    private final Object lock = new Object();     // must be final!

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

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


答案 2

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


推荐