在Java中,在代码中声明关键部分的惯用方法如下:
private void doSomething() { // thread-safe code synchronized(this) { // thread-unsafe code } // thread-safe code }
几乎所有的块都同步 this
,但是有什么特别的原因吗?还有其他可能吗?是否有关于同步对象的最佳实践?(比如私人实例Object
?)
正如早期的回答者所指出的那样,最佳做法是在有限范围的对象上进行同步(换句话说,选择你可以使用的最严格的范围,并使用它.)特别是,同步this
是一个坏主意,除非您打算允许您班级的用户获得锁定.
但是,如果你选择在a上进行同步,则会出现一个特别难看的情况java.lang.String
.字符串可以(实际上几乎总是)实习.这意味着在ENTIRE JVM中,每个相同内容的字符串在幕后都是相同的字符串.这意味着如果您同步任何String,另一个(完全不同的)代码部分也会锁定具有相同内容的String,实际上也会锁定您的代码.
我曾经在生产系统中对死锁进行故障排除,并且(非常痛苦地)跟踪两个完全不同的开源软件包的死锁,每个软件包都在一个内容都是String的String实例上同步"LOCK"
.
首先,请注意以下代码段是相同的.
public void foo() { synchronized (this) { // do something thread-safe } }
和:
public synchronized void foo() { // do something thread-safe }
做同样的事情.除了代码可读性和样式之外,对它们中的任何一个都没有偏好.
当您同步方法或代码块时,重要的是要知道为什么要做这样的事情,以及您正在锁定什么对象,以及用于何种目的.
另请注意,在某些情况下,您需要客户端同步代码块,其中您要求的监视器(即同步对象)不一定this
,如下例所示:
Vector v = getSomeGlobalVector(); synchronized (v) { // some thread-safe operation on the vector }
我建议你对并发编程有更多的了解,一旦你确切知道幕后发生了什么,它将为你提供很多帮助.你应该查看Java中的Concurrent Programming,这是一本关于这个主题的好书.如果您想快速了解主题,请查看Java Concurrency @ Sun
我试图避免同步,this
因为这将允许来自外部的所有参与该对象的人阻止我的同步.相反,我创建了一个本地同步对象:
public class Foo { private final Object syncObject = new Object(); … }
现在我可以使用该对象进行同步,而不必担心任何人"窃取"锁定.
只是为了强调Java中还有ReadWriteLocks,可以找到java.util.concurrent.locks.ReadWriteLock.
在我的大部分用法中,我将锁定分为"阅读"和"更新".如果您只使用synchronized关键字,则对同一方法/代码块的所有读取都将"排队".一次只能有一个线程访问该块.
在大多数情况下,如果您只是在阅读,就不必担心并发问题.当你在写作时,你担心并发更新(导致数据丢失),或者在写入(部分更新)期间读取,你必须担心.
因此,在多线程编程期间,读/写锁对我来说更有意义.