在 Java 临界区中,我应该同步什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/416183/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 14:22:31  来源:igfitidea点击:

In Java critical sections, what should I synchronize on?

javamultithreadingsynchronized

提问by lindelof

In Java, the idiomatic way to declare critical sections in the code is the following:

在 Java 中,在代码中声明临界区的惯用方法如下:

private void doSomething() {
  // thread-safe code
  synchronized(this) {
    // thread-unsafe code
  }
  // thread-safe code
}

Almost all blocks synchronize on this, but is there a particular reason for this? Are there other possibilities? Are there any best practices on what object to synchronize on? (such as private instances of Object?)

几乎所有块都在 上同步 this,但是否有特殊原因?还有其他可能吗?是否有关于要同步的对象的最佳实践?(例如Object? 的私有实例)

采纳答案by Yuval Adam

First, note that the following code snippets are identical.

首先,请注意以下代码片段是相同的。

public void foo() {
    synchronized (this) {
        // do something thread-safe
    }
}

and:

和:

public synchronized void foo() {
    // do something thread-safe
}

do exactly the same thing. No preference for either one of them except for code readability and style.

同样的事情。除了代码可读性和风格之外,不偏爱其中任何一个。

When you do synchronize methods or blocks of code, it's important to know whyyou are doing such a thing, and what objectexactly you are locking, and for what purpose.

当您同步方法或代码块时,重要的是要知道您为什么要这样做,以及您锁定的究竟是什么对象,以及用于什么目的

Also note that there are situations in which you will want to client-side synchronizeblocks of code in which the monitor you are asking for (i.e. the synchronized object) is not necessarily this, like in this example :

另请注意,在某些情况下,您需要在客户端同步代码块,其中您要求的监视器(即同步对象)不一定this,例如本例:

Vector v = getSomeGlobalVector();
synchronized (v) {
    // some thread-safe operation on the vector
}

I suggest you get more knowledge about concurrent programming, it will serve you a great deal once you know exactly what's happening behind the scenes. You should check out Concurrent Programming in Java, a great book on the subject. If you want a quick dive-in to the subject, check out Java Concurrency @ Sun

我建议您了解更多有关并发编程的知识,一旦您确切了解幕后发生的事情,它将为您提供很多帮助。您应该查看Java 中的并发编程,这是一本关于该主题的好书。如果您想快速深入了解该主题,请查看Java Concurrency @ Sun

回答by Bombe

I try to avoid synchronizing on thisbecause that would allow everybody from the outside who had a reference to that object to block my synchronization. Instead, I create a local synchronization object:

我尽量避免同步,this因为这将允许外部引用该对象的每个人阻止我的同步。相反,我创建了一个本地同步对象:

public class Foo {
    private final Object syncObject = new Object();
    …
}

Now I can use that object for synchronization without fear of anybody “stealing” the lock.

现在我可以使用该对象进行同步,而不必担心任何人“窃取”锁。

回答by Electric Monk

You'll want to synchronize on an object that can serve as a Mutex. If the current instance (the thisreference) is suitable (not a Singleton, for instance), you may use it, as in Java any Object may serve as the Mutex.

您需要在可用作互斥锁的对象上进行同步。如果当前实例(this引用)合适(例如不是单例),您可以使用它,因为在 Java 中任何对象都可以用作互斥锁。

In other occasions, you may want to share a Mutex between several classes, if instances of these classes may all need access to the same resources.

在其他情况下,您可能希望在多个类之间共享互斥锁,如果这些类的实例可能都需要访问相同的资源。

It depends a lot on the environment you're working in and the type of system you're building. In most Java EE applications I've seen, there's actually no real need for synchronization...

这在很大程度上取决于您工作的环境和您正在构建的系统类型。在我见过的大多数 Java EE 应用程序中,实际上并没有真正需要同步......

回答by eljenso

Synchronization in Java often involves synchronizing operations on the same instance. Synchronizing on thisthen is very idiomatic since thisis a shared reference that is automatically available between different instance methods (or sections of) in a class.

Java 中的同步通常涉及对同一实例的同步操作。同步thisthen 是非常惯用的,因为它this是一个共享引用,它在类中的不同实例方法(或部分)之间自动可用。

Using another reference specifically for locking, by declaring and initializing a private field Object lock = new Object()for example, is something I never needed or used. I think it is only useful when you need external synchronization on two or more unsynchronized resources inside an object, although I would always try to refactor such a situation into a simpler form.

使用另一个专门用于锁定的引用,例如通过声明和初始化私有字段Object lock = new Object(),是我从未需要或使用过的。我认为只有当您需要对对象内的两个或多个未同步资源进行外部同步时,它才有用,尽管我总是尝试将这种情况重构为更简单的形式。

Anyway, implicit (synchronized method) or explicit synchronized(this)is used a lot, also in the Java libraries. It is a good idiom and, if applicable, should always be your first choice.

无论如何,隐式(同步方法)或显式synchronized(this)被大量使用,也在 Java 库中使用。这是一个很好的习语,如果适用,应该始终是您的首选。

回答by Kent Lai

Just to highlight that there are also ReadWriteLocks available in Java, found as java.util.concurrent.locks.ReadWriteLock.

只是为了强调在 Java 中也有可用的 ReadWriteLock,如 java.util.concurrent.locks.ReadWriteLock。

In most of my usage, I seperate my locking as 'for reading' and 'for updates'. If you simply use a synchronized keyword, all reads to the same method/code block will be 'queued'. Only one thread can access the block at one time.

在我的大部分使用中,我将锁定分开为“用于阅读”和“用于更新”。如果您仅使用同步关键字,则对同一方法/代码块的所有读取都将“排队”。一次只有一个线程可以访问该块。

In most cases, you never have to worry about concurrency issues if you are simply doing reading. It is when you are doing writing that you worry about concurrent updates (resulting in lost of data), or reading during a write (partial updates), that you have to worry about.

在大多数情况下,如果您只是在做阅读,则永远不必担心并发问题。当你在写的时候,你担心并发更新(导致数据丢失),或者在写期间读(部分更新),你必须担心。

Therefore a read/write lock makes more sense to me during multi-threaded programming.

因此,在多线程编程期间,读/写锁对我来说更有意义。

回答by Jared

As earlier answerers have noted, it is best practice to synchronize on an object of limited scope (in other words, pick the most restrictive scope you can get away with, and use that.) In particular, synchronizing on thisis a bad idea, unless you intend to allow the users of your class to gain the lock.

正如前面的回答者所指出的,最佳做法是在有限范围的对象上进行同步(换句话说,选择您可以摆脱的最严格的范围,并使用它。)特别是,同步this是一个坏主意,除非您打算允许您班级的用户获得锁定。

A particularly ugly case arises, though, if you choose to synchronize on a java.lang.String. Strings can be (and in practice almost always are) interned. That means that each string of equal content - in the ENTIRE JVM- turns out to be the same string behind the scenes. That means that if you synchronize on any String, another (completely disparate) code section that also locks on a String with the same content, will actually lock your code as well.

但是,如果您选择在java.lang.String. 字符串可以(并且在实践中几乎总是)被实习。这意味着每个内容相同的字符串——在整个 JVM 中——在幕后都是相同的字符串。这意味着如果您在任何字符串上同步,另一个(完全不同的)代码部分也锁定具有相同内容的字符串,实际上也会锁定您的代码。

I was once troubleshooting a deadlock in a production system and (very painfully) tracked the deadlock to two completely disparate open source packages that each synchronized on an instance of String whose contents were both "LOCK".

我曾经对生产系统中的死锁进行故障排除,并且(非常痛苦地)将死锁跟踪到两个完全不同的开源包,每个包都在一个 String 实例上同步,其内容都是"LOCK".

回答by Lawrence Dol

Personally, I think the answers which insist that it is never or only rarely correct to sync on thisare misguided. I think it depends on your API. If your class is a threadsafe implementation and you so document it, then you should use this. If the synchronization is not to make each instance of the class as a whole threadsafe in the invocation of it's public methods, then you should use a private internal object. Reusable library components oftenfall into the former category - you must think carefully before you disallow the user to wrap your API in external synchronization.

就个人而言,我认为坚持同步永远不会或很少正确的答案this是错误的。我认为这取决于您的 API。如果你的类是一个线程安全的实现并且你记录了它,那么你应该使用this. 如果同步不是为了在调用它的公共方法时使类的每个实例作为一个整体线程安全,那么您应该使用私有内部对象。可重用的库组件通常属于前一类 - 在禁止用户将 API 包装在外部同步中之前,您必须仔细考虑。

In the former case, using thisallows multiple methods to be invoked in an atomic manner. One example is PrintWriter, where you may want to output multiple lines (say a stack trace to the console/logger) and guarantee they appear together - in this case the fact that it hides the sync object internally is a real pain. Another such example are the synchronized collection wrappers - there you must synchronize on the collection object itself in order to iterate; since iteration consists of multiple method invocations you cannotprotect it totally internally.

在前一种情况下, usingthis允许以原子方式调用多个方法。一个例子是 PrintWriter,你可能想要输出多行(比如控制台/记录器的堆栈跟踪)并保证它们一起出现 - 在这种情况下,它在内部隐藏同步对象的事实是一个真正的痛苦。另一个这样的例子是同步集合包装器——你必须在集合对象本身上同步才能迭代;由于迭代由多个方法调用组成,因此您无法完全在内部对其进行保护。

In the latter case, I use a plain object:

在后一种情况下,我使用一个普通对象:

private Object mutex=new Object();

However, having seen many JVM dumps and stack traces that say a lock is "an instance of java.lang.Object()" I have to say that using an inner class might often be more helpful, as others have suggested.

但是,在看到许多 JVM 转储和堆栈跟踪都说锁是“java.lang.Object() 的一个实例”之后,我不得不说,正如其他人所建议的那样,使用内部类通常可能更有帮助。

Anyway, that's my two bits worth.

无论如何,那是我的两点价值。

Edit: One other thing, when synchronizing on thisI prefer to sync the methods, and keep the methods very granular. I think it's clearer and more concise.

编辑:另一件事,在同步时this我更喜欢同步方法,并保持方法非常细化。我认为它更清晰,更简洁。

回答by Hans-Peter St?rr

On what you synchronize depends on what other threads that might potentially get into conflict with this method call can synchronize.

您同步的内容取决于可能与此方法调用发生冲突的其他线程可以同步的内容。

If thisis an object that is used by only one thread and we are accessing a mutable object which is shared between threads, a good candidate is to synchronize over that object - synchronizing on thishas no point since another thread that modifies that shared object might not even know this, but does know that object.

如果this是一个仅被一个线程使用的对象,并且我们正在访问一个在线程之间共享的可变对象,那么一个好的候选对象是对该对象进行同步 - 同步this没有意义,因为修改该共享对象的另一个线程甚至可能不会知道this,但确实知道那个对象。

On the other hand synchronizing over thismakes sense if many threads call methods of this object at the same time, for instance if we are in a singleton.

另一方面,this如果许多线程同时调用此对象的方法,则同步是有意义的,例如,如果我们处于单例状态。

Note that a syncronized method is often not the best option, since we hold a lock the whole time the method runs. If it contains timeconsuming but thread safe parts, and a not so time consuming thread-unsafe part, synchronizing over the method is very wrong.

请注意,同步方法通常不是最佳选择,因为我们在方法运行的整个过程中都持有锁。如果它包含耗时但线程安全的部分,以及一个不那么耗时的线程不安全部分,那么同步方法是非常错误的。

回答by Ravindra babu

Almost all blocks synchronize on this, but is there a particular reason for this? Are there other possibilities?

几乎所有块都同步于此,但是否有特殊原因?还有其他可能吗?

This declaration synchronizes entire method.

此声明同步整个方法。

private synchronized void doSomething() {

This declaration synchronized a part of code block instead of entire method.

这个声明同步了代码块的一部分而不是整个方法。

private void doSomething() {
  // thread-safe code
  synchronized(this) {
    // thread-unsafe code
  }
  // thread-safe code
}

From oracle documentation page

来自 oracle 文档页面

making these methods synchronized has two effects:

使这些方法同步有两个效果:

First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.

首先,对同一对象的同步方法的两次调用不可能交错。当一个线程正在为一个对象执行同步方法时,所有其他调用同一个对象的同步方法的线程都会阻塞(挂起执行),直到第一个线程完成对对象的处理。

Are there other possibilities? Are there any best practices on what object to synchronize on? (such as private instances of Object?)

还有其他可能吗?是否有关于要同步的对象的最佳实践?(例如 Object 的私有实例?)

There are many possibilities and alternatives to synchronization. You can make your code thread safe by using high level concurrency APIs( available since JDK 1.5 release)

同步有许多可能性和替代方案。您可以通过使用高级并发API(自 JDK 1.5 版本起可用)来使您的代码线程安全

Lock objects
Executors
Concurrent collections
Atomic variables
ThreadLocalRandom

Refer to below SE questions for more details:

有关更多详细信息,请参阅以下 SE 问题:

Synchronization vs Lock

同步与锁定

Avoid synchronized(this) in Java?

在 Java 中避免同步(这个)?

回答by epox

the Best Practices is to create an object solely to provide the lock:

最佳实践是创建一个仅提供锁的对象:

private final Object lock = new Object();

private void doSomething() {
  // thread-safe code
  synchronized(lock) {
    // thread-unsafe code
  }
  // thread-safe code
}

By doing this you are safe, that no calling code can ever deadlock your method by an unintentional synchronized(yourObject)line.

通过这样做,您是安全的,任何调用代码都不会因无意的synchronized(yourObject)行而使您的方法死锁。

(Credits to @jared and @yuval-adam who explained this in more details above.)

感谢@jared 和@yuval-adam,他们在上面更详细地解释了这一点。

My guess is that the popularity of using thisin tutorials came from early Sun javadoc: https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html

我的猜测是this在教程中使用的流行来自早期的 Sun javadoc:https: //docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html