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

时间:2021-11-16 06:47:52

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 need to have methods: public synchronized setInstanceVar() and public synchronized getInstanceVar() ?

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

Thanks in advance for your answers.

提前感谢您的回答。

6 个解决方案

#1


19  

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 or make the variable volatile. (It avoids other threads getting "stale" data.)

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

#2


36  

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

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

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

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

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

This is bad for two reasons:

这有两个原因:

  • All synchronized methods within the same class use the exact same lock, which reduces throughput
  • 同一类中的所有同步方法使用完全相同的锁,这会降低吞吐量

  • Anyone can get access to the lock, including members of other classes.
  • 任何人都可以访问锁,包括其他类的成员。

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

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

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

Within that synchronized block, I am holding the lock which is required by all synchronized methods within MyClass. This further reduces throughput and dramatically increases the chances of a deadlock.

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

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

更好的方法是拥有一个专用的锁对象并直接使用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.Lock interface and the java.util.concurrent.locks.ReentrantLock implementation 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上是相同的)。

#3


3  

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

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

private volatile int instanceVar =0;

public synchronized setInstanceVar() { instanceVar++;

}

#4


1  

. 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 Practice is excellent, and certainly much more reliable than me.

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

#5


1  

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.

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

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和设置方法示例中,您可以执行以下操作:

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

#6


-3  

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中,对int的操作是原子的,所以没有,在这种情况下,如果你所做的只是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的另一部分。

#1


19  

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 or make the variable volatile. (It avoids other threads getting "stale" data.)

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

#2


36  

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

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

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

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

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

This is bad for two reasons:

这有两个原因:

  • All synchronized methods within the same class use the exact same lock, which reduces throughput
  • 同一类中的所有同步方法使用完全相同的锁,这会降低吞吐量

  • Anyone can get access to the lock, including members of other classes.
  • 任何人都可以访问锁,包括其他类的成员。

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

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

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

Within that synchronized block, I am holding the lock which is required by all synchronized methods within MyClass. This further reduces throughput and dramatically increases the chances of a deadlock.

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

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

更好的方法是拥有一个专用的锁对象并直接使用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.Lock interface and the java.util.concurrent.locks.ReentrantLock implementation 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上是相同的)。

#3


3  

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

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

private volatile int instanceVar =0;

public synchronized setInstanceVar() { instanceVar++;

}

#4


1  

. 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 Practice is excellent, and certainly much more reliable than me.

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

#5


1  

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.

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

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和设置方法示例中,您可以执行以下操作:

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

#6


-3  

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中,对int的操作是原子的,所以没有,在这种情况下,如果你所做的只是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的另一部分。