Java线程-同步问题
来自Sun的教程:
同步方法启用了一种防止线程干扰和内存一致性错误的简单策略:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都通过同步方法完成。 (一个重要的例外:final 字段在对象构造后无法修改,一旦构造了对象,就可以通过非同步方法安全地读取)这种策略是有效的,但可能会带来活性问题,因为我们将请参阅本课程后面的内容。
Q1.上述语句是否意味着,如果一个类的对象要在多个线程之间共享,那么该类的所有实例方法(最终字段的 getters 除外)都应该同步,因为实例方法流程实例变量?
From Sun's tutorial:
Synchronized methods enable a simple strategy for preventing thread interference and memory consistency errors: if an object is visible to more than one thread, all reads or writes to that object's variables are done through synchronized methods. (An important exception: final fields, which cannot be modified after the object is constructed, can be safely read through non-synchronized methods, once the object is constructed) This strategy is effective, but can present problems with liveness, as we'll see later in this lesson.
Q1. Is the above statements mean that if an object of a class is going to be shared among multiple threads, then all instance methods of that class (except getters of final fields) should be made synchronized, since instance methods process instance variables?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
为了了解 Java 中的并发性,我推荐宝贵的Java 并发性实践。
针对您的具体问题,尽管同步所有方法是实现线程安全的一种快速而肮脏的方法,但它根本无法很好地扩展。考虑一下备受诟病的 Vector 类。每个方法都是同步的,而且效果很糟糕,因为迭代仍然不是线程安全的。
In order to understand concurrency in Java, I recommend the invaluable Java Concurrency in Practice.
In response to your specific question, although synchronizing all methods is a quick-and-dirty way to accomplish thread safety, it does not scale well at all. Consider the much maligned Vector class. Every method is synchronized, and it works terribly, because iteration is still not thread safe.
不。这意味着同步方法是实现线程安全的一种方法,但它们不是唯一的方法,并且它们本身并不能保证在所有情况下的完全安全。
No. It means that synchronized methods are a way to achieve thread safety, but they're not the only way and, by themselves, they don't guarantee complete safety in all situations.
未必。例如,您可以同步(例如,在专用对象上放置锁)访问对象变量的方法的一部分。在其他情况下,您可以将作业委托给一些已经处理同步问题的内部对象。
有很多选择,这完全取决于您要实现的算法。尽管如此,“同步”关键字通常是最简单的一种。
编辑
没有全面的教程,每种情况都是独一无二的。学习它就像学习一门外语:永无止境:)
但肯定有有用的资源。特别是 Heinz Kabutz 的网站上有一系列有趣的文章。
http://www.javaspecialists.eu/archive/Issue152.html
(请参阅页面上的完整列表)
如果其他人有任何链接,我也有兴趣查看。我发现整个主题非常令人困惑(并且可能是核心 java 中最困难的部分),特别是因为 java 5 中引入了新的并发机制。
玩得开心!
Not necessarily. You can synchronize (e.g. place a lock on dedicated object) part of the method where you access object's variables, for example. In other cases, you may delegate job to some inner object(s) which already handles synchronization issues.
There are lots of choices, it all depends on the algorithm you're implementing. Although, 'synchronized' keywords is usually the simplest one.
edit
There is no comprehensive tutorial on that, each situation is unique. Learning it is like learning a foreign language: never ends :)
But there are certainly helpful resources. In particular, there is a series of interesting articles on Heinz Kabutz's website.
http://www.javaspecialists.eu/archive/Issue152.html
(see the full list on the page)
If other people have any links I'd be interested to see also. I find the whole topic to be quite confusing (and, probably, most difficult part of core java), especially since new concurrency mechanisms were introduced in java 5.
Have fun!
最一般的形式是的。
不可变对象不需要同步。
此外,您可以对可变实例变量(或其组)使用单独的监视器/锁,这将有助于提高活力。以及仅同步数据更改的部分,而不是整个方法。
In the most general form yes.
Immutable objects need not be synchronized.
Also, you can use individual monitors/locks for the mutable instance variables (or groups there of) which will help with liveliness. As well as only synchronize the portions where data is changed, rather than the entire method.
synchronized methodName 与synchronized( object )
这是正确的,并且是一种替代方案。我认为仅同步对该对象的访问而不是同步其所有方法会更有效。
这将很有用。
虽然差异可能很微妙,但如果您在单个线程中使用相同的对象,即(在方法上使用同步关键字),
当这样定义一个类时,一次只有一个线程可以调用
单击
方法。如果在单线程应用程序中过于频繁地调用此方法会发生什么情况?您将花费一些额外的时间来检查该线程是否可以在不需要时获得对象锁。
在这种情况下,将不必要地调用检查线程是否可以锁定对象的
Integer.MAX_VALUE
(2 147 483 647 ) 次。所以在这种情况下去掉synchronized关键字会运行得更快。
那么,在多线程应用程序中如何做到这一点呢?
您只需同步对象:
Vector vs ArrayList
作为补充说明,这种用法(syncrhonized methodName vs.syncrhonized( object ))是,顺便说一下,原因之一为什么
java.util.Vector
现在被java.util.ArrayList
取代。许多Vector
方法都是同步的。大多数情况下,列表是在单线程应用程序或代码段中使用的(即jsp/servlet内的代码在单线程中执行),并且Vector的额外同步对性能没有帮助。
Hashtable
被HashMap
替换也是如此synchronized methodName vs synchronized( object )
That's correct, and is one alternative. I think it would be more efficient to synchronize access to that object only instead synchronize all it's methods.
While the difference may be subtle, it would be useful if you use that same object in a single thread
ie ( using synchronized keyword on the method )
When a class is defined like this, only one thread at a time may invoke the
click
method.What happens if this method is invoked too frequently in a single threaded app? You'll spend some extra time checking if that thread can get the object lock when it is not needed.
In this case, the check to see if the thread can lock the object will be invoked unnecessarily
Integer.MAX_VALUE
( 2 147 483 647 ) times.So removing the synchronized keyword in this situation will run much faster.
So, how would you do that in a multithread application?
You just synchronize the object:
Vector vs ArrayList
As an additional note, this usage ( syncrhonized methodName vs. syncrhonized( object ) ) is, by the way, one of the reasons why
java.util.Vector
is now replaced byjava.util.ArrayList
. Many of theVector
methods are synchronized.Most of the times a list is used in a single threaded app or piece of code ( ie code inside jsp/servlets is executed in a single thread ), and the extra synchronization of Vector doesn't help to performance.
Same goes for
Hashtable
being replaced byHashMap
事实上,getter a 也应该同步,或者字段将变得
易失性
。这是因为当您获得某些值时,您可能对该值的最新版本感兴趣。您会看到,同步块语义不仅提供了执行的原子性(例如,它保证一次只有一个线程执行此块),而且提供了可见性。这意味着当线程进入同步块时,它会使本地缓存失效,当它退出时,它会将所有已修改的变量转储回主内存。易失性变量具有相同的可见性语义。In fact getters a should be synchronized too or fields are to be made
volatile
. That is because when you get some value, you're probably interested in a most recent version of the value. You see,synchronized
block semantics provides not only atomicity of execution (e.g. it guarantees that only one thread executes this block at one time), but also a visibility. It means that when thread enterssynchronized
block it invalidates its local cache and when it goes out it dumps any variables that have been modified back to main memory.volatile
variables has the same visibility semantics.不。即使 getter 也必须同步,除非它们仅访问最终字段。原因是,例如,当访问一个 long 值时,另一个线程当前正在写入它,并且您在读取它时仅写入了前 4 个字节,而其他 4 个字节仍然是旧值,因此发生了微小的变化。
No. Even getters have to be synchronized, except when they access only final fields. The reason is, that, for example, when accessing a long value, there is a tiny change that another thread currently writes it, and you read it while just the first 4 bytes have been written while the other 4 bytes remain the old value.
是的,这是正确的。所有修改数据或访问可能被不同线程修改的数据的方法都需要在同一监视器上同步。
最简单的方法是将方法标记为同步。如果这些是长时间运行的方法,您可能只想同步读取/写入的部分。在这种情况下,您将定义监视器以及 wait() 和 notification()。
Yes, that's correct. All methods that modify data or access data that may be modified by a different thread need to be synchronized on the same monitor.
The easy way is to mark the methods as synchronized. If these are long-running methods, you may want to only synchronize that parts that the the reading/writing. In this case you would definie the monitor, along with wait() and notify().
简单的答案是肯定的。
如果类的对象要被多个线程共享,则需要同步 getter 和 setter 以防止数据不一致。
如果所有线程都有单独的对象副本,则无需同步方法。如果您的实例方法不仅仅是设置和获取,则必须分析线程等待长时间运行的 getter/setter 完成的威胁。
The simple answer is yes.
If an object of the class is going to be shared by multiple threads, you need to syncronize the getters and setters to prevent data inconsistency.
If all the threads would have seperate copy of object, then there is no need to syncronize the methods. If your instance methods are more than mere set and get, you must analyze the threat of threads waiting for a long running getter/setter to finish.
您可以使用同步方法、同步块、并发工具(例如信号量),或者如果您真的想深入了解,可以使用原子引用。其他选项包括将成员变量声明为
volatile
以及使用AtomicInteger
等类而不是Integer
。这一切都取决于具体情况,但是有多种可用的并发工具 - 这些只是其中的一些。
同步可能会导致保持等待死锁,其中两个线程各自拥有一个对象的锁,并试图获取另一个线程对象的锁。
对于类来说,同步也必须是全局的,一个容易犯的错误就是忘记同步方法。当一个线程持有一个对象的锁时,其他线程仍然可以访问该对象的非同步方法。
You could use synchronized methods, synchronized blocks, concurrency tools such as
Semaphore
or if you really want to get down and dirty you could use Atomic References. Other options include declaring member variables asvolatile
and using classes likeAtomicInteger
instead ofInteger
.It all depends on the situation, but there are a wide range of concurrency tools available - these are just some of them.
Synchronization can result in hold-wait deadlock where two threads each have the lock of an object, and are trying to acquire the lock of the other thread's object.
Synchronization must also be global for a class, and an easy mistake to make is to forget to synchronize a method. When a thread holds the lock for an object, other threads can still access non synchronized methods of that object.