Java 同步与锁定

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4201713/
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-14 14:29:44  来源:igfitidea点击:

Synchronization vs Lock

javamultithreadingconcurrencysynchronizationjava.util.concurrent

提问by daydreamer

java.util.concurrentAPI provides a class called as Lock, which would basically serialize the control in order to access the critical resource. It gives method such as park()and unpark().

java.util.concurrentAPI 提供了一个名为 as 的类Lock,它基本上会序列化控件以访问关键资源。它给出了诸如park()and 之类的方法unpark()

We can do similar things if we can use synchronizedkeyword and using wait()and notify() notifyAll()methods.

我们可以做类似的事情,如果我们可以使用synchronized关键字,并使用wait()notify() notifyAll()方法。

I am wondering which one of these is better in practice and why?

我想知道其中哪一个在实践中更好,为什么?

采纳答案by Steven Schlansker

If you're simply locking an object, I'd prefer to use synchronized

如果您只是锁定一个对象,我更愿意使用 synchronized

Example:

例子:

Lock.acquire();
doSomethingNifty(); // Throws a NPE!
Lock.release(); // Oh noes, we never release the lock!

You have to explicitly do try{} finally{}everywhere.

你必须在try{} finally{}任何地方明确地做。

Whereas with synchronized, it's super clear and impossible to get wrong:

而使用同步,它非常清晰且不可能出错:

synchronized(myObject) {
    doSomethingNifty();
}

That said, Locks may be more useful for more complicated things where you can't acquire and release in such a clean manner. I would honestly prefer to avoid using bare Locks in the first place, and just go with a more sophisticated concurrency control such as a CyclicBarrieror a LinkedBlockingQueue, if they meet your needs.

也就是说,Lock对于更复杂的事物,您无法以如此干净的方式获取和释放,s 可能更有用。老实说,我更愿意首先避免使用裸Locks ,如果它们满足您的需要,只需使用更复杂的并发控制,例如 aCyclicBarrier或 a LinkedBlockingQueue

I've never had a reason to use wait()or notify()but there may be some good ones.

我从来没有理由使用wait()notify()但可能有一些好的。

回答by Thilo

You can achieve everything the utilities in java.util.concurrentdo with the low-level primitives like synchronized, volatile, or wait/ notify

您可以实现一切在公用事业java.util.concurrent中做的低级原像synchronizedvolatile等待/通知

However, concurrency is tricky, and most people get at least some parts of it wrong, making their code either incorrect or inefficient (or both).

然而,并发是棘手的,大多数人至少会出错,导致他们的代码不正确或效率低下(或两者兼而有之)。

The concurrent API provides a higher-level approach, which is easier (and as such safer) to use. In a nutshell, you should not need to use synchronized, volatile, wait, notifydirectly anymore.

并发 API 提供了一种更高级别的方法,使用起来更容易(因此更安全)。简而言之,您应该不再需要synchronized, volatile, wait, notify直接使用了。

The Lockclass itself is on the lower-level side of this toolbox, you may not even need to use that directly either (you can use Queuesand Semaphoreand stuff, etc, most of the time).

类本身就是对这个工具箱的下级侧,你甚至可能不会需要使用直接或者(你可以用Queues信号量和材料等,大部分的时间)。

回答by Bert F

I am wondering which one of these is better in practice and why?

我想知道其中哪一个在实践中更好,为什么?

I've found that Lockand Condition(and other new concurrentclasses) are just more tools for the toolbox. I could do most everything I needed with my old claw hammer (the synchronizedkeyword), but it was awkward to use in some situations. Several of those awkward situations became much simpler once I added more tools to my toolbox: a rubber mallet, a ball-peen hammer, a prybar, and some nail punches. However, my old claw hammer still sees its share of use.

我发现Lockand Condition(和其他新concurrent类)只是工具箱的更多工具。我可以用我的旧羊角锤(synchronized关键字)完成我需要的大部分工作,但在某些情况下使用起来很尴尬。一旦我在我的工具箱中添加了更多工具:橡胶锤、球头锤、撬杆和一些钉子冲头,这些尴尬的情况就变得简单多了。但是,我的旧羊角锤仍然可以使用。

I don't think one is really "better" than the other, but rather each is a better fit for different problems. In a nutshell, the simple model and scope-oriented nature of synchronizedhelps protect me from bugs in my code, but those same advantages are sometimes hindrances in more complex scenarios. Its these more complex scenarios that the concurrent package was created to help address. But using this higher level constructs requires more explicit and careful management in the code.

我不认为一个真的比另一个“更好”,而是每个都更适合不同的问题。简而言之,简单的模型和面向范围的特性synchronized有助于保护我免受代码中的错误的影响,但在更复杂的场景中,这些相同的优势有时会成为障碍。创建并发包以帮助解决这些更复杂的场景。但是使用这种更高级别的构造需要在代码中进行更明确和仔细的管理。

===

===

I think the JavaDocdoes a good job of describing the distinction between Lockand synchronized(the emphasis is mine):

我认为JavaDoc中确实描述之间的区别的一个好工作Locksynchronized(重点是我的):

Lock implementations provide more extensive locking operationsthan can be obtained using synchronized methods and statements. They allow more flexible structuring, may have quite different properties, and may support multiple associated Condition objects.

...

The use of synchronized methodsor statements provides access to the implicit monitor lock associated with every object, but forces all lock acquisition and release to occur in a block-structured way: when multiple locksare acquiredthey must be released in the opposite order, and all locks must be released in the same lexical scope in which they were acquired.

While the scoping mechanism for synchronizedmethods and statements makes it much easier to program with monitor locks, and helps avoid many common programming errors involving locks, there are occasions where you need to work with locks in a more flexible way. For example, **some algorithms*for traversing concurrently accessed data structures require the use of "hand-over-hand" or "chain locking": you acquire the lock of node A, then node B, then release A and acquire C, then release B and acquire D and so on. Implementations of the Lock interfaceenable the use of such techniques by allowing a lock to be acquired and released in different scopes, and allowing multiple locks to be acquired and released in any order.

With this increased flexibility comes additional responsibility. The absence of block-structured locking removes the automatic release of locksthat occurs with synchronized methods and statements. In most cases, the following idiom should be used:

...

When locking and unlocking occur in different scopes, care must be taken to ensurethat all code that is executed while the lock is held is protected by try-finally or try-catchto ensure that the lock is releasedwhen necessary.

Lock implementations provide additional functionalityover the use of synchronized methods and statements by providing a non-blocking attempt to acquirea lock (tryLock()), an attempt to acquire the lock that can be interrupted(lockInterruptibly(), and an attempt to acquire the lock that can timeout(tryLock(long, TimeUnit)).

...

锁实现提供比使用同步方法和语句可以获得的更广泛的锁定操作。它们允许更灵活的结构化,可能具有完全不同的属性,并且可能支持多个关联的 Condition 对象

...

使用同步方法或语句提供访问与每个对象相关联的隐式监视器锁,但是在一个块结构的方式来发生强制所有锁获取和释放:当多个锁获取它们必须以相反的顺序被释放,并所有锁都必须在它们被获取的同一个词法范围内释放

虽然同步方法和语句的作用域机制使使用监视器锁编程变得更加容易,并有助于避免许多涉及锁的常见编程错误,但在某些情况下,您需要以更灵活的方式使用锁。例如,用于遍历并发访问的数据结构的**某些算法*需要使用“交接手”或“链锁”:您先获取节点 A 的锁,然后是节点 B,然后释放 A 并获取 C,然后释放 B 并获得 D,依此类推。所述的实施方式中锁定接口通过使得能够使用这样的技术允许获得并在不同的范围释放锁,并允许以任何顺序获取和释放多个锁

有了这个增加的灵活性带来了更多的责任块结构锁缺失消除了同步方法和语句发生的锁的自动释放。在大多数情况下,应使用以下习语:

...

锁定和解锁发生在不同的作用域时,必须注意确保持有锁时执行的所有代码都受到 try-finally 或 try-catch 的保护,确保在必要时释放锁

锁定实现提供附加功能通过提供一种对使用同步方法和语句非阻塞试图获取锁(的tryLock()),以试图获取可中断锁定)(的lockInterruptibly(和试图获取可以超时的锁(tryLock(long, TimeUnit))。

...

回答by John Vint

There are 4 main factors into why you would want to use synchronizedor java.util.concurrent.Lock.

有 4 个主要因素会影响您为什么要使用synchronizedjava.util.concurrent.Lock

Note: Synchronized locking is what I mean when I say intrinsic locking.

注意:同步锁定就是我所说的内在锁定的意思。

  1. When Java 5 came out with ReentrantLocks, they proved to have quite a noticeble throughput difference then intrinsic locking. If youre looking for faster locking mechanism and are running 1.5 consider j.u.c.ReentrantLock. Java 6's intrinsic locking is now comparable.

  2. j.u.c.Lock has different mechanisms for locking. Lock interruptable - attempt to lock until the locking thread is interrupted; timed lock - attempt to lock for a certain amount of time and give up if you do not succeed; tryLock - attempt to lock, if some other thread is holding the lock give up. This all is included aside from the simple lock. Intrinsic locking only offers simple locking

  3. Style. If both 1 and 2 do not fall into categories of what you are concerned with most people, including myself, would find the intrinsic locking semenatics easier to read and less verbose then j.u.c.Lock locking.
  4. Multiple Conditions. An object you lock on can only be notified and waited for a single case. Lock's newCondition method allows for a single Lock to have mutliple reasons to await or signal. I have yet to actually need this functionality in practice, but is a nice feature for those who need it.
  1. 当 Java 5 推出 ReentrantLocks 时,事实证明它们在吞吐量上与内在锁有相当明显的差异。如果您正在寻找更快的锁定机制并且正在运行 1.5,请考虑 jucReentrantLock。Java 6 的内在锁定现在具有可比性。

  2. jucLock 有不同的锁定机制。Lock interruptable - 尝试锁定直到锁定线程被中断;定时锁定 - 尝试锁定一定时间,如果不成功则放弃;tryLock - 尝试锁定,如果其他线程持有锁定放弃。除了简单的锁之外,这一切都包括在内。内在锁只提供简单的锁

  3. 风格。如果 1 和 2 都不属于您所关心的类别,包括我自己在内的大多数人会发现内在锁定语义比 jucLock 锁定更易于阅读且不那么冗长。
  4. 多重条件。您锁定的对象只能收到通知并等待单个案例。Lock 的 newCondition 方法允许单个 Lock 有多种原因等待或发出信号。在实践中我还没有真正需要这个功能,但对于那些需要它的人来说,这是一个很好的功能。

回答by Brian Tarbox

The main difference is fairness, in other words are requests handled FIFO or can there be barging? Method level synchronization ensures fair or FIFO allocation of the lock. Using

主要区别在于公平性,换句话说,请求是 FIFO 处理的还是可以有闯入?方法级同步确保锁的公平或先进先出分配。使用

synchronized(foo) {
}

or

或者

lock.acquire(); .....lock.release();

does not assure fairness.

不保证公平。

If you have lots of contention for the lock you can easily encounter barging where newer requests get the lock and older requests get stuck. I've seen cases where 200 threads arrive in short order for a lock and the 2nd one to arrive got processed last. This is ok for some applications but for others it's deadly.

如果您对锁有很多争用,您很容易遇到闯入,其中新请求获得锁而旧请求卡住。我见过有 200 个线程在短时间内到达以获取锁而第 2 个到达的线程最后被处理的情况。这对于某些应用程序来说没问题,但对于其他应用程序来说却是致命的。

See Brian Goetz's "Java Concurrency In Practice" book, section 13.3 for a full discussion of this topic.

有关此主题的完整讨论,请参阅 Brian Goetz 的“Java Concurrency In Practice”一书的 13.3 节。

回答by bodrin

Brian Goetz's "Java Concurrency In Practice" book, section 13.3: "...Like the default ReentrantLock, intrinsic locking offers no deterministic fairness guarantees, but the statistical fairness guarantees of most locking implementations are good enough for almost all situations..."

Brian Goetz 的“Java Concurrency In Practice”一书,第 13.3 节:“……与默认的 ReentrantLock 一样,内在锁定不提供确定性的公平性保证,但大多数锁定实现的统计公平性保证对于几乎所有情况都足够好……”

回答by Ravindra babu

I would like to add some more things on top of Bert Fanswer.

我想在Bert F答案之上添加更多内容 。

Lockssupport various methods for finer grained lock control, which are more expressive than implicit monitors (synchronizedlocks)

Locks支持更细粒度的锁控制的各种方法,这些方法比隐式监视器(synchronized锁)更具表现力

A Lock provides exclusive access to a shared resource: only one thread at a time can acquire the lock and all access to the shared resource requires that the lock be acquired first. However, some locks may allow concurrent access to a shared resource, such as the read lock of a ReadWriteLock.

Lock 提供对共享资源的独占访问:一次只有一个线程可以获取锁,所有对共享资源的访问都需要先获取锁。但是,某些锁可能允许并发访问共享资源,例如 ReadWriteLock 的读锁。

Advantages of Lock over Synchronizationfrom documentation page

从文档页面锁定优于同步的优点

  1. The use of synchronized methods or statements provides access to the implicit monitor lock associated with every object, but forces all lock acquisition and release to occur in a block-structured way

  2. Lock implementations provide additional functionality over the use of synchronized methods and statements by providing a non-blocking attempt to acquire a lock (tryLock()), an attempt to acquire the lock that can be interrupted (lockInterruptibly(), and an attempt to acquire the lock that can timeout (tryLock(long, TimeUnit)).

  3. A Lock class can also provide behavior and semantics that is quite different from that of the implicit monitor lock, such as guaranteed ordering, non-reentrant usage, or deadlock detection

  1. 同步方法或语句的使用提供对与每个对象关联的隐式监视器锁的访问,但强制所有锁的获取和释放以块结构的方式发生

  2. 锁实现提供了使用同步方法和语句的附加功能,通过提供非阻塞尝试获取lock (tryLock()),尝试获取可以被中断的锁(lockInterruptibly(),以及尝试获取可以timeout (tryLock(long, TimeUnit)).

  3. Lock 类还可以提供与隐式监视器锁完全不同的行为和语义,例如保证排序、不可重入使用或死锁检测

ReentrantLock: In simple terms as per my understanding, ReentrantLockallows an object to re-enter from one critical section to other critical section . Since you already have lock to enter one critical section, you can other critical section on same object by using current lock.

ReentrantLock:根据我的理解,简单来说,ReentrantLock允许一个对象从一个临界区重新进入另一个临界区。由于您已经拥有进入一个临界区的锁,因此您可以使用当前锁对同一对象的其他临界区进行锁定。

ReentrantLockkey features as per this article

ReentrantLock根据本文的主要功能

  1. Ability to lock interruptibly.
  2. Ability to timeout while waiting for lock.
  3. Power to create fair lock.
  4. API to get list of waiting thread for lock.
  5. Flexibility to try for lock without blocking.
  1. 能够中断锁定。
  2. 能够在等待锁定时超时。
  3. 创建公平锁定的权力。
  4. 获取等待锁的线程列表的 API。
  5. 灵活地尝试锁定而不阻塞。

You can use ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLockto further acquire control on granular locking on read and write operations.

您可以使用 ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock进一步获取对读写操作的粒度锁定的控制。

Apart from these three ReentrantLocks, java 8 provides one more Lock

除了这三个 ReentrantLocks,java 8 还提供了一个 Lock

StampedLock:

冲压锁:

Java 8 ships with a new kind of lock called StampedLock which also support read and write locks just like in the example above. In contrast to ReadWriteLock the locking methods of a StampedLock return a stamp represented by a long value.

Java 8 附带了一种称为 StampedLock 的新型锁,它也支持读写锁,就像上面的例子一样。与 ReadWriteLock 相比,StampedLock 的锁定方法返回一个由 long 值表示的戳记。

You can use these stamps to either release a lock or to check if the lock is still valid. Additionally stamped locks support another lock mode called optimistic locking.

您可以使用这些标记来释放锁定或检查锁定是否仍然有效。此外,标记锁支持另一种称为乐观锁的锁模式。

Have a look at this articleon usage of different type of ReentrantLockand StampedLocklocks.

看看这篇关于不同类型ReentrantLockStampedLock锁的用法的文章

回答by Md Monjur Ul Hasan

Lock makes programmers life easier. Here are few situations that can be achieved easier with lock.

Lock 让程序员的生活更轻松。以下是使用锁可以更轻松地实现的几种情况。

  1. Lock in one method, and release the lock in other method.
  2. You have two threads working on two different pieces of code, however first thread there is dependency on there second thread to complete certain piece of code before it proceed any further (while some other threads also working simultaneously). A shared lock can solve this problem quite easily.
  3. Implementing monitors. For example, a simple queue where the put and get methods are executed from many different threads. However, do you want to over lap neither the same methods one on another, nor both put and get methods can overlap. In such case a private lock makes the life very easier.
  1. 一种方法锁定,另一种方法释放锁定。
  2. 您有两个线程处理两个不同的代码段,但是第一个线程依赖于第二个线程在它继续进行之前完成某些代码段(而其他一些线程也同时工作)。共享锁可以很容易地解决这个问题。
  3. 实施监视器。例如,一个简单的队列,其中 put 和 get 方法从许多不同的线程执行。但是,您是否不想将相同的方法相互重叠,也不要让 put 和 get 方法重叠。在这种情况下,私人锁使生活变得更加轻松。

While, the lock, and conditions are build on the synchronized. So certainly you can achiever the same goal with that. However, that might make your life difficult and can deviate you from solving the actual problem.

同时,锁和条件建立在同步上。所以当然你可以实现同样的目标。但是,这可能会使您的生活变得困难,并使您无法解决实际问题。

回答by NKumar

Major difference between lock and synchronized:

锁和同步的主要区别:

  • with locks, you can release and acquire the locks in any order.
  • with synchronized, you can release the locks only in the order it was acquired.
  • 使用锁,您可以按任意顺序释放和获取锁。
  • 使用同步,您只能按照获取的顺序释放锁。

回答by Shyam

Lock and synchronize block both serves the same purpose but it depends on the usage. Consider the below part

锁定和同步块都具有相同的目的,但这取决于用法。考虑以下部分

void randomFunction(){
.
.
.
synchronize(this){
//do some functionality
}

.
.
.
synchronize(this)
{
// do some functionality
}


} // end of randomFunction

In the above case , if a thread enters the synchronize block, the other block is also locked. If there are multiple such synchronize block on the same object, all the blocks are locked. In such situations , java.util.concurrent.Lock can be used to prevent unwanted locking of blocks

在上述情况下,如果一个线程进入同步块,则另一个块也被锁定。如果同一个对象上有多个这样的同步块,则所有块都被锁定。在这种情况下,java.util.concurrent.Lock 可用于防止不必要的块锁定。