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

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

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


当前回答

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

其他回答

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

如果只是锁定一个对象,我更喜欢使用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()的理由,但可能有一些好的理由。

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

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

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


} // end of randomFunction

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

我想在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等等,大多数时候)。