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

发布于 2024-07-09 17:31:16 字数 492 浏览 15 评论 0原文

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

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

例如,如果我的类

public class MyClass {
    private int instanceVar = 0;

    public setInstanceVar()
    {
        instanceVar++;
    }

    public getInstanceVar()
    {
        return instanceVar;
    }
}

在什么情况下(使用类MyClass)我需要有方法: 公共同步setInstanceVar()公共同步 getInstanceVar()

预先感谢您的回答。

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() ?

Thanks in advance for your answers.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

优雅的叶子 2024-07-16 17:31:16

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

问题在于,synchronized 方法实际上只是用于获取 this 锁定并在方法执行期间保持锁定的语法糖。 因此,公共同步无效 setInstanceVar() 相当于这样:

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

这很糟糕,原因有两个:

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

没有什么可以阻止我在另一个类中执行类似的操作:

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

在该 synchronized 块中,我持有 中所有 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 上也是一样的)。

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.

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:

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.

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

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).

坚持沉默 2024-07-16 17:31:16

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

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.)

孤城病女 2024-07-16 17:31:16

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

private volatile int instanceVar =0;

public synchronized setInstanceVar() { instanceVar++;

}

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.

private volatile int instanceVar =0;

public synchronized setInstanceVar() { instanceVar++;

}
雾里花 2024-07-16 17:31:16

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

 synchronized increment()
 { 
       i++
 }

 synchronized get()
 {
   return i;
  }

但这实际上在这里不起作用,因为为了确保你的调用者线程获得相同的值递增,您必须保证以原子方式递增然后检索,而您在这里没有这样做 - 即您必须执行类似的操作

  synchronized int {
    increment
    return get()
  }

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

这本书Java并发实践非常好,而且肯定比我可靠得多。

. 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:

 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.

轻拂→两袖风尘 2024-07-16 17:31:16

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

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

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

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

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:

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;
    }
}
樱花细雨 2024-07-16 17:31:16

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

如果这些是长整型或双精度型,则确实需要同步,因为长整型/双精度型的一部分可能会被更新,然后有另一个线程读取,最后长整型/双精度型的另一部分会被更新。

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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文