Java中哪些情况需要同步方法访问?
什么情况下需要同步实例成员的访问? 我知道对类的静态成员的访问始终需要同步 - 因为它们在类的所有对象实例之间共享。
我的问题是如果不同步实例成员什么时候会出错?
例如,如果我的类
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()
andpublic synchronized getInstanceVar()
?
Thanks in advance for your answers.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
synchronized
修饰符确实是一个坏主意,应该不惜一切代价避免。 我认为值得赞扬的是,Sun 试图使锁定更容易实现,但同步只会带来更多的麻烦,而不是其价值。问题在于,
synchronized
方法实际上只是用于获取this
锁定并在方法执行期间保持锁定的语法糖。 因此,公共同步无效 setInstanceVar() 相当于这样:这很糟糕,原因有两个:
没有什么可以阻止我在另一个类中执行类似的操作:
在该
synchronized
块中,我持有中所有
。 这进一步降低了吞吐量,并且显着增加了死锁的可能性。synchronized
方法所需的锁我的类更好的方法是拥有一个专用的
lock
对象并直接使用synchronized(...)
块:或者,您可以使用
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, butsynchronized
just causes more trouble than it is worth.The issue is that a
synchronized
method is actually just syntax sugar for getting the lock onthis
and holding it for the duration of the method. Thus,public synchronized void setInstanceVar()
would be equivalent to something like this:This is bad for two reasons:
synchronized
methods within the same class use the exact same lock, which reduces throughputThere is nothing to prevent me from doing something like this in another class:
Within that
synchronized
block, I am holding the lock which is required by allsynchronized
methods withinMyClass
. 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 thesynchronized(...)
block directly:Alternatively, you can use the
java.util.concurrent.Lock
interface and thejava.util.concurrent.locks.ReentrantLock
implementation to achieve basically the same result (in fact, it is the same on Java 6).这取决于您是否希望您的类是线程安全的。 大多数类不应该是线程安全的(为了简单起见),在这种情况下您不需要同步。 如果您需要它是线程安全的,您应该同步访问或使变量可变。 (它可以避免其他线程获取“陈旧”数据。)
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.)
如果你想让这个类线程安全,我会将
instanceVar
声明为volatile
以确保你始终从内存中获得最新的值,而且我还会将setInstanceVar()
synchronized
因为在 JVM 中增量不是原子操作。If you want to make this class thread safe I would declare
instanceVar
asvolatile
to make sure you get always the most updated value from memory and also I would make thesetInstanceVar()
synchronized
because in the JVM an increment is not an atomic operation.。 粗略地说,答案是“视情况而定”。 在这里同步你的 setter 和 getter 只会达到保证多个线程无法在彼此增量操作之间读取变量的预期目的:
但这实际上在这里不起作用,因为为了确保你的调用者线程获得相同的值递增,您必须保证以原子方式递增然后检索,而您在这里没有这样做 - 即您必须执行类似的操作
基本上,同步对于定义需要保证运行的操作很有用线程安全(换句话说,您不能创建一个单独的线程破坏您的操作并使您的类行为不合逻辑,或者破坏您期望的数据状态的情况)。 这实际上是一个比这里可以讨论的更大的话题。
这本书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:
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
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.util.concurrent.locks.ReentrantReadWriteLock ——理论上它允许多个线程同时读取,但仅限于允许一个线程写入。 因此,在每个人似乎都给出的 getter 和 set 方法示例中,您可以执行以下操作:
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 中,对 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.