为什么使用ReentrantLock如果可以使用synchronized(this)?
我试图了解是什么使得并发locking如此重要,如果可以使用synchronized (this)
。 在下面的虚拟代码中,我可以这样做:
- 同步整个方法或同步易受攻击的区域(synchronized(this){…})
- 或用ReentrantLocklocking易受攻击的代码区域。
码:
private final ReentrantLock lock = new ReentrantLock(); private static List<Integer> ints; public Integer getResult(String name) { . . . lock.lock(); try { if (ints.size()==3) { ints=null; return -9; } for (int x=0; x<ints.size(); x++) { System.out.println("["+name+"] "+x+"/"+ints.size()+". values >>>>"+ints.get(x)); } } finally { lock.unlock(); } return random; }
ReentrantLock是非结构化的 ,与synchronized
构造不同 – 即不需要使用块结构进行locking,甚至可以在方法中保持locking。 一个例子:
private ReentrantLock lock; public void foo() { ... lock.lock(); ... } public void bar() { ... lock.unlock(); ... }
这样的stream程不可能通过synchronized
构造中的单个监视器来表示。
除此之外, ReentrantLock
支持locking轮询和支持超时的可中断locking等待 。 ReentrantLock
还支持可configuration的公平策略 ,允许更灵活的线程调度。
这个类的构造函数接受一个可选的公平参数。 如果设置为
true
,则在争用中,locking允许授予对最长等待线程的访问权限。 否则,这个锁并不能保证任何特定的访问顺序。 使用许multithreading访问的公平锁的程序可能会显示较低的整体吞吐量(即速度较慢,通常速度较慢),但使用默认设置的时间差异较小,并且保证缺less饥饿。 但是请注意,锁的公平性并不能保证线程调度的公平性。 因此,使用公平锁的许multithreading中的一个可以连续多次获得它,而其他活动线程不在进行中,并且当前没有锁。 另请注意,不定时的tryLock
方法不符合公平性设置。 即使其他线程正在等待locking,也会成功。
ReentrantLock
也可能更具可扩展性 ,在更高的争用条件下performance更好。 你可以在这里阅读更多。
然而,这个说法有争议。 看到下面的评论:
在可重入lockingtesting中,每次创build一个新的locking,因此没有独占locking,结果数据无效。 此外,IBM链接不提供底层基准的源代码,因此无法确定testing是否正确执行。
什么时候应该使用ReentrantLock
? 根据那篇developerWorks文章…
答案非常简单 – 当你真正需要它提供的
synchronized
时不需要的东西时,使用它 – 例如定时locking等待,可中断locking等待,非块结构locking,多个条件variables或locking轮询。ReentrantLock
也具有可扩展性的好处,如果实际上存在争用性较高的情况,则应该使用它,但请记住绝大多数synchronized
块几乎不存在任何争用,更不用说高度争用。 我build议开发同步,直到同步已被certificate是不够的,而不是简单地假设“性能会更好”,如果你使用ReentrantLock
。 请记住,这些是高级用户的高级工具。 (真正的高级用户往往更喜欢他们能find的最简单的工具,直到他们确信简单的工具是不够的。)一如既往,先把它做对,然后担心是否要加快速度。
即使已经通过input其他代码块已经获得了锁,一个可重入的锁将允许锁持有人input代码块。 一个不可重入的锁将拥有自己的锁持有者块,因为它将不得不释放从另一个代码块获得的锁以重新获得相同的锁以进入需要代码块的嵌套锁
public synchronized void functionOne() { // do something functionTwo(); // do something else // redundant, but permitted... synchronized(this) { // do more stuff } } public synchronized void functionTwo() { // do even more stuff! }
可重入锁的扩展function包括: –
- 每个监视器具有多个条件variables的能力。 使用synchronized关键字的监视器只能有一个。 这意味着重入锁支持多个wait()/ notify()队列。
- 锁的能力“公平”。 “[公平]锁意味着授予访问最长等待线程的权限,否则这个锁并不保证任何特定的访问顺序。 同步块是不公平的。
- 能否检查锁是否被保留。
- 获得等待锁的线程列表的能力。
重入锁的缺点是:
需要添加导入语句。 需要在try / finally块中封装locking采集。 这使得它比synchronized关键字更难看。 synchronized关键字可以放在方法定义中,避免需要一个减less嵌套的块。
何时使用: –
- 如果需要实现遍历链表的线程,locking下一个节点,然后解锁当前节点,ReentrantLock可能更易于使用。
- 同步关键字适用于locking粗化等情况,通过逃逸分析提供适应性旋转,偏移locking和locking隐患的可能性。 这些优化目前不针对ReentrantLock实现。
欲了解更多信息 。
ReentrantReadWriteLock
是一个专门的锁,而synchornized(this)
是一个通用的锁。 他们是相似的,但不完全相同。
你是对的,你可以使用synchronized(this)
而不是ReentrantReadWriteLock
但是反过来并不总是如此。
如果您想更好地了解ReentrantReadWriteLock
专门查找有关生产者 – 使用者线程同步的一些信息。
一般而言,您可以记住全方法同步和通用同步(使用synchronized
关键字)可以在大多数应用程序中使用,而不用过多考虑同步的语义,但是如果您需要从代码中榨取性能,则可能需要探索其他更精细的或专用的同步机制。
顺便说一下,使用synchronized(this
) – 通常使用公共类实例进行locking – 可能会有问题,因为它会将代码打开到潜在的死锁,因为其他人不知道可能会试图locking对象的其他位置程序。
从关于ReentrantLock的 oracle文档页面:
可重入的互斥锁具有与使用同步方法和语句访问的隐式监视器锁相同的基本行为和语义,但具有扩展function。
-
一个ReentrantLock由上一次成功locking的线程拥有,但尚未解锁。 调用锁的线程将返回,成功获取锁,当锁不是由另一个线程拥有。 如果当前线程已经拥有该锁,该方法将立即返回。
-
这个类的构造函数接受一个可选的公平参数。 如果设置为true,则在争用中, locking允许授予对最长等待线程的访问权限 。 否则,这个锁并不能保证任何特定的访问顺序。
ReentrantLock按照这篇文章的关键特性
- 能够中断地locking。
- 在等待locking时能够超时。
- 力量创造公平的锁。
- API获取锁等待线程列表。
- 灵活地尝试locking,而不会阻塞。
您可以使用ReentrantReadWriteLock.ReadLock,ReentrantReadWriteLock.WriteLock进一步获取对读取和写入操作的粒度locking的控制。
看看Benjamen关于不同typesReentrantLocks使用情况的文章
您可以使用公平策略或超时的重入锁来避免线程匮乏。 您可以应用线程公平策略。 这将有助于避免一个线程永远等待到您的资源。
private final ReentrantLock lock = new ReentrantLock(true); //the param true turns on the fairness policy.
“公平策略”select下一个可执行的线程来执行。 这是基于优先,从上次运行时间,等等等等
另外,同步可以无限地阻止,如果它不能逃脱块。 Reentrantlock可以有超时设置。