concurrent API提供了一个名为Lock的类,它将序列化控件以访问关键资源。它给出了park()和unpark()等方法。

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

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


当前回答

锁和同步的主要区别:

使用锁,您可以以任何顺序释放和获取锁。 使用synchronized,您只能按照获取锁的顺序释放锁。

其他回答

如果只是锁定一个对象,我更喜欢使用synchronized

例子:

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

你必须显式地在所有地方执行try{} finally{}。

而对于synchronized,它非常清楚,不可能出错:

synchronized(myObject) {
    doSomethingNifty();
}

也就是说,对于更复杂的事情,锁可能更有用,因为你无法以如此干净的方式获取和释放。老实说,我更倾向于在一开始就避免使用裸锁,而只是使用更复杂的并发控制,如CyclicBarrier或LinkedBlockingQueue,如果它们满足您的需求的话。

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

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

Lock in one method, and release the lock in another method. If You have two threads working on two different pieces of code, however, in the first thread has a pre-requisite on a certain piece of code in the second thread (while some other threads also working on the same piece of code in the second thread simultaneously). A shared lock can solve this problem quite easily. Implementing monitors. For example, a simple queue where the put and get methods are executed from many other threads. However, you do not want multiple put (or get) methods running simultaneously, neither the put and get method running simultaneously. A private lock makes your life a lot easier to achieve this.

同时,锁和条件建立在同步机制之上。因此,当然可以实现与使用锁相同的功能。但是,使用同步解决复杂的场景可能会使您的生活变得困难,并可能使您偏离解决实际问题的方向。

我想在Bert F答案的基础上再补充一些东西。

锁支持用于更细粒度锁控制的各种方法,这些方法比隐式监控器(同步锁)更具表现力。

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

从文档页面来看,锁定相对于同步的优势

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 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)). 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

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

ReentrantLock键的特性如本文所述

能够可中断地锁定。 能力超时,而等待锁定。 创造公平锁的权力。 API来获取等待锁的线程列表。 灵活地尝试锁定而不阻塞。

你可以使用ReentrantReadWriteLock。ReadLock ReentrantReadWriteLock。WriteLock以进一步获得对读写操作的粒度锁的控制。

除了这三个reentrantlock之外,java 8还提供了另外一个Lock

StampedLock:

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

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

本文介绍了不同类型的ReentrantLock和StampedLock锁的用法。

你可以实现java.util.concurrent中的实用程序使用低级原语(如synchronized、volatile或wait / notify)所做的所有事情

然而,并发性是棘手的,大多数人至少在某些方面犯了错误,导致他们的代码要么不正确,要么效率低下(或两者兼而有之)。

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

Lock类本身在这个工具箱的底层,你甚至不需要直接使用它(你可以使用queue和Semaphore等等,大多数时候)。

Brian Goetz的《Java并发实践》一书,第13.3节: "...像默认的ReentrantLock一样,内在锁不提供确定性的公平性保证,但是 大多数锁定实现的统计公平性保证对于几乎所有情况都足够好……”