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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/309631/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 11:53:33  来源:igfitidea点击:

What Cases Require Synchronized Method Access in Java?

javaconcurrencysynchronizationmethodsnon-static

提问by user39732

In what cases is it necessary to synchronize access to instance members? I understand that access to static members of a class always needs to be synchronized- because they are shared across all object instances of the class.

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

My question is when would I be incorrect if I do not synchronize instance members?

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

for example if my class is

例如,如果我的班级是

public class MyClass {
    private int instanceVar = 0;

    public setInstanceVar()
    {
        instanceVar++;
    }

    public getInstanceVar()
    {
        return instanceVar;
    }
}

in what cases (of usage of the class MyClass) would I needto have methods: public synchronized setInstanceVar()and public synchronized getInstanceVar()?

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

Thanks in advance for your answers.

预先感谢您的回答。

回答by Daniel Spiewak

The synchronizedmodifier is really a badidea and should be avoided at all costs. I think it is commendable that Sun tried to make locking a little easier to acheive, but synchronizedjust causes more trouble than it is worth.

synchronized修饰符实在是一个糟糕的主意,应该不惜一切代价避免。我认为值得称赞的是,Sun 试图使锁定更容易实现,但synchronized只会带来更多的麻烦。

The issue is that a synchronizedmethod is actually just syntax sugar for getting the lock on thisand holding it for the duration of the method. Thus, public synchronized void setInstanceVar()would be equivalent to something like this:

问题在于,synchronized方法实际上只是用于this在方法的持续时间内获取锁定并保持锁定的语法糖。因此,public synchronized void setInstanceVar()将等同于这样的事情:

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

This is bad for two reasons:

这很糟糕,原因有二:

  • All synchronizedmethods within the same class use the exact same lock, which reduces throughput
  • Anyone can get access to the lock, including members of other classes.
  • synchronized同一类中的所有方法都使用完全相同的锁,这会降低吞吐量
  • 任何人都可以访问锁,包括其他班级的成员。

There is nothing to prevent me from doing something like this in another class:

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

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

Within that synchronizedblock, I am holding the lock which is required by all synchronizedmethods within MyClass. This further reduces throughput and dramaticallyincreases the chances of a deadlock.

在该synchronized块,我拿的是由所有需要的锁synchronized中的方法MyClass。这进一步降低了吞吐量并显着增加了死锁的机会。

A better approach is to have a dedicated lockobject and to use the synchronized(...)block directly:

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

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

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

Alternatively, you can use the java.util.concurrent.Lockinterface and the java.util.concurrent.locks.ReentrantLockimplementation to achieve basically the same result (in fact, it is the same on Java 6).

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

回答by Jon Skeet

It depends on whether you want your class to be thread-safe. Most classes shouldn't be thread-safe (for simplicity) in which case you don't need synchronization. If you need it to be thread-safe, you should synchronize access ormake the variable volatile. (It avoids other threads getting "stale" data.)

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

回答by bruno conde

If you want to make this class thread safe I would declare instanceVaras volatileto make sure you get always the most updated value from memory and also I would make the setInstanceVar()synchronizedbecause in the JVM an increment is not an atomic operation.

如果你想使这个类线程安全的,我就会宣布instanceVarvolatile确保您始终获得从内存中的最新价值,也是我会做setInstanceVar()synchronized,因为在JVM的增量不是一个原子操作。

private volatile int instanceVar =0;

public synchronized setInstanceVar() { instanceVar++;

}

回答by Steve B.

. Roughly, the answer is "it depends". Synchronizing your setter and getter here would only have the intended purpose of guaranteeing that multiple threads couldn't read variables between each others increment operations:

. 粗略地说,答案是“视情况而定”。在这里同步你的 setter 和 getter 的预期目的只是保证多个线程不能在彼此递增操作之间读取变量:

 synchronized increment()
 { 
       i++
 }

 synchronized get()
 {
   return i;
  }

but that wouldn't really even work here, because to insure that your caller thread got the same value it incremented, you'd have to guarantee that you're atomically incrementing and then retrieving, which you're not doing here - i.e you'd have to do something like

但这在这里甚至不起作用,因为为了确保您的调用者线程获得与它增加的值相同的值,您必须保证您正在原子地增加然后检索,而您在这里没有这样做 - 即您必须做类似的事情

  synchronized int {
    increment
    return get()
  }

Basically, synchronization is usefull for defining which operations need to be guaranteed to run threadsafe (inotherwords, you can't create a situation where a separate thread undermines your operation and makes your class behave illogically, or undermines what you expect the state of the data to be). It's actually a bigger topic than can be addressed here.

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

This book Java Concurrency in Practiceis excellent, and certainly much more reliable than me.

Java Concurrency in Practice》这本书很棒,而且肯定比我可靠得多。

回答by Steve B.

To simply put it, you use synchronized when you have mutliple threads accessing the same method of the same instance which will change the state of the object/or application.

简而言之,当您有多个线程访问同一实例的相同方法时,您将使用同步,这将更改对象/或应用程序的状态。

It is meant as a simple way to prevent race conditions between threads, and really you should only use it when you are planning on having concurrent threads accessing the same instance, such as a global object.

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

Now when you are reading the state of an instance of a object with concurrent threads, you may want to look into the the java.util.concurrent.locks.ReentrantReadWriteLock -- which in theory allows many threads to read at a time, but only one thread is allowed to write. So in the getter and setting method example that everyone seems to be giving, you could do the following:

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

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;
    }
}

回答by Outlaw Programmer

In Java, operations on ints are atomic so no, in this case you don't need to synchronize if all you're doing is 1 write and 1 read at a time.

在 Java 中,对整数的操作是原子的,所以不,在这种情况下,如果您一次只做 1 次写入和 1 次读取,则不需要同步。

If these were longs or doubles, you do need to synchronize because it's possible for part of the long/double to be updated, then have another thread read, then finally the other part of the long/double updated.

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