Answers:
如果您只是锁定一个对象,我更喜欢使用 synchronized
例:
Lock.acquire();
doSomethingNifty(); // Throws a NPE!
Lock.release(); // Oh noes, we never release the lock!
您必须在try{} finally{}
任何地方都明确地做。
而使用同步,则非常清晰,不可能出错:
synchronized(myObject) {
doSomethingNifty();
}
也就是说,Lock
对于无法以这种干净的方式获取和释放的更复杂的事物,s可能会更有用。老实说,我宁愿避免首先使用Lock
s,而可以使用更复杂的并发控制(例如a CyclicBarrier
或a LinkedBlockingQueue
)来满足您的需求。
我从未有过使用的理由,wait()
或者notify()
可能有一些不错的理由。
std::lock_guard
我想知道其中哪一个在实践中更好,为什么?
我发现Lock
和Condition
(以及其他新concurrent
类)只是该工具箱的更多工具。我可以用旧的羊角锤(synchronized
关键字)来完成大部分所需的工作,但是在某些情况下使用起来很尴尬。一旦我在工具箱中添加了更多工具,其中一些尴尬的情况就变得更加简单:橡胶槌,圆头锤,撬棒和一些打孔器。但是,我的旧羊角锤仍然可以使用。
我不认为一个人真的比另一个人“更好”,但是每个人都更适合不同的问题。简而言之,的简单模型和面向范围的性质synchronized
有助于保护我免受代码中的错误的侵害,但是在更复杂的场景中,这些相同的优点有时会成为障碍。创建并发包以帮助解决这些更复杂的情况。但是,使用这种更高级别的结构需要对代码进行更明确,更仔细的管理。
===
我认为JavaDoc中确实描述之间的区别的一个好工作Lock
和synchronized
(重点是我的):
与使用同步方法和语句相比,锁实现提供了更广泛的锁操作。它们允许更灵活的结构,可以具有完全不同的属性,并且可以支持多个关联的Condition对象。
...
使用同步方法或语句可访问与每个对象关联的隐式监视器锁,但会强制所有锁的获取和释放以块结构的方式发生:当获取多个锁时,它们必须以相反的顺序释放,并且所有锁必须在获得它们的相同词汇范围内释放。
尽管用于同步方法和语句的作用域机制使使用监视器锁编程变得更加容易,并且有助于避免许多常见的涉及锁的编程错误,但在某些情况下,您需要以更灵活的方式使用锁。例如,用于遍历并发访问的数据结构的某些算法需要使用“交接”或“链锁”:您先获得节点A的锁,然后获得节点B的锁,然后释放A并获得C,然后释放B并获得D,依此类推。所述的实施方式中锁定接口通过使得能够使用这样的技术允许获得并在不同的范围释放锁,并允许以任意顺序获取和释放多个锁。
有了这个增加的灵活性带来了更多的责任。在不存在块结构锁定的去除锁的自动释放,与同步方法和语句发生。在大多数情况下,应使用以下惯用法:
...
当锁定和解锁发生在不同的范围内时,必须小心以确保通过try-finally或try-catch保护持有锁定时执行的所有代码,以确保在必要时释放锁定。
锁实现通过使用非阻塞尝试获取锁(tryLock()),尝试获取可以被中断的锁(lockInterruptible()以及尝试获取锁),提供了比同步方法和语句更多的功能。可能超时的锁(tryLock(long,TimeUnit))。
...
您可以实现一切在公用事业java.util.concurrent中 做的低级原像synchronized
,volatile
或等待 / 通知
但是,并发是棘手的,大多数人至少会误认为其中的某些部分,使他们的代码不正确或效率低下(或两者兼而有之)。
并发API提供了更高级别的方法,该方法更易于使用(因此更加安全)。简而言之,您不再需要synchronized, volatile, wait, notify
直接使用。
该锁 类本身就是对这个工具箱的下级侧,你甚至可能不会需要使用直接或者(你可以用Queues
和信号量和材料等,大部分的时间)。
您要使用synchronized
或的原因有4个主要因素java.util.concurrent.Lock
。
注意:当我说内在锁定时,我指的是同步锁定。
当Java 5推出ReentrantLocks时,事实证明它们与固有锁定相比在吞吐量上有显着差异。如果您正在寻找更快的锁定机制并正在运行1.5,请考虑使用jucReentrantLock。Java 6的固有锁定现在是可比较的。
jucLock具有不同的锁定机制。锁定可中断-尝试锁定直到锁定线程被中断;定时锁定-尝试锁定一定的时间,如果失败则放弃;tryLock-尝试锁定,如果其他线程持有该锁定则放弃。除了简单的锁以外,所有这些都包括在内。本质锁定仅提供简单的锁定
我想在Bert F答案的基础上添加更多内容 。
Locks
支持各种用于更细粒度的锁控制的方法,这些方法比隐式监视器(synchronized
锁)更具表现力
锁提供对共享资源的独占访问:一次只能有一个线程可以获取该锁,对共享资源的所有访问都需要首先获取该锁。但是,某些锁可能允许并发访问共享资源,例如ReadWriteLock的读锁。
文档页面上的“同步锁定”的优点
使用同步方法或语句可访问与每个对象关联的隐式监视器锁,但强制所有锁的获取和释放以块结构方式进行
锁实现通过使用非阻塞尝试获取a lock (tryLock())
,尝试获取可以被中断的锁(lockInterruptibly()
以及尝试获取可以锁)来提供与同步方法和语句相比更多的功能timeout (tryLock(long, TimeUnit))
。
Lock类还可以提供与隐式监视器锁完全不同的行为和语义,例如保证顺序,不可重用或死锁检测
ReentrantLock:根据我的理解,简单而言,它ReentrantLock
允许对象从一个关键部分重新输入到另一关键部分。由于您已经具有进入一个关键部分的锁定,因此可以使用当前锁定在同一对象上的其他关键部分。
ReentrantLock
主要特点为每本文章
您可以 ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock
用来进一步获取对读写操作的粒度锁定的控制。
除了这三个ReentrantLocks,Java 8还提供了一个Lock
盖章锁:
Java 8附带了一种称为StampedLock的新型锁,它也支持读写锁,就像上面的示例一样。与ReadWriteLock相比,StampedLock的锁定方法返回以长值表示的图章。
您可以使用这些标记来释放锁或检查锁是否仍然有效。另外,加盖式锁支持另一种称为乐观锁的锁模式。
主要区别在于公平性,换句话说,请求是按FIFO处理还是可以进行插入?方法级别同步可确保公平分配或FIFO分配锁。使用
synchronized(foo) {
}
要么
lock.acquire(); .....lock.release();
不保证公平。
如果您对锁有很多争执,那么您很容易在新请求获得锁而旧请求陷入阻塞的过程中遇到插入问题。我见过这样的情况,其中200个线程在短时间内到达一个锁,而第二个线程最后被处理。对于某些应用程序这是可以的,但对于其他应用程序则是致命的。
有关此主题的完整讨论,请参见Brian Goetz的“ Java Concurrency in Practice”(第13.3节)。
锁使程序员的生活更轻松。通过锁定可以轻松实现以下几种情况。
While,锁和条件建立在同步的基础上。因此,您当然可以达到相同的目标。但是,这可能会使您的生活变得困难,并使您无法解决实际的问题。