我试图理解,如果可以使用synchronized(this),是什么使得并发锁如此重要。在下面的伪代码中,我可以执行以下任一操作:
- 同步整个方法或同步易受攻击区域(
synchronized(this){…}) - 或者使用ReentrantLock锁定易受攻击的代码区域
代码:
private final ReentrantLock lock=new ReentrantLock();
私有静态列表<;整数>;ints;
公共整数getResult(字符串名称){
.
.
.
lock.lock();
试一试{
如果(整数大小()==3){
ints=null;
返回-9;
}
对于(int x=0;x<;int.size();x++){
System.out.println(“[”+name+“]”“+x+”/“+ints.size()+”.values>;>;>;”+ints.get(x));
}
}最后{
lock.unlock();
}
返回随机;
}
AReentrantLock是非结构化的,不同于同步的构造,即不需要使用块结构进行锁定,甚至可以跨方法持有锁。例如:
私有重入锁;
公共图书馆{
...
lock.lock();
...
}
公共空白栏(){
...
lock.unlock();
...
}
这种流不可能通过同步的结构中的单个监视器来表示
除此之外,ReentrantLock还支持锁定轮询http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantLock.html#tryLock(长,%20java.util.concurrent.TimeUnit)“rel=“noreferrer”>支持超时的可中断锁等待ReentrantLock也支持可配置的公平性策略,允许更灵活的线程调度
此类的构造函数接受可选的公平性参数。当设置
true时,在争用下,锁有利于授予对等待时间最长的线程的访问权限。否则,此锁不保证任何特定的访问顺序。使用公平锁的程序可能会显示较低的总体吞吐量(即,速度较慢;通常较慢)与使用默认设置的线程相比,具有更小的获得锁的时间差异,并保证没有饥饿。但是,请注意,锁的公平性并不保证线程调度的公平性。因此,使用公平锁的多个线程中的一个线程可能会连续多次获得公平锁,而其他活动线程则不会继续进行当前未持有锁。还要注意,untimedtryLock方法不支持公平性设置。如果锁可用,即使其他线程正在等待,它也会成功
ReentrantLock也可能是更具可扩展性,在更高的争用下表现得更好。您可以阅读更多有关这方面的信息这里
然而,这一主张受到质疑;见以下评论:
在可重入锁测试中,每次都会创建一个新锁,因此没有独占锁,结果数据无效。此外,IBM link没有提供底层基准的源代码,因此无法确定测试是否正确执行
什么时候应该使用ReentrantLocks?根据developerWorks的那篇文章
答案很简单——当您实际需要它提供的
synchronized不需要的东西时使用它,如定时锁等待、可中断锁等待、非块结构化锁、多个条件变量或锁轮询。ReentrantLock还具有可伸缩性优势,如果您确实有e出现高争用的情况,但请记住,绝大多数synchronized块几乎不会出现任何争用,更不用说高争用了。我建议使用同步进行开发,直到证明同步不足为止,而不是简单地假设“性能会更好”;如果您使用的是ReentrantLock。请记住,这些是面向高级用户的高级工具。(真正的高级用户倾向于选择他们能找到的最简单的工具,直到他们确信这些简单的工具是不足够的。)一如既往,首先要做正确的事情,然后担心是否必须加快速度
最后一个将在不久的将来变得更加相关的方面与Java 15和Project Loom。在(新的)在虚拟线程的世界中,底层调度程序使用ReentrantLock比使用synchronized工作得更好,至少在最初的Java15版本中是这样,但以后可能会进行优化
在当前的绝缘线束实现中,可以在两种情况下固定虚拟线程:当堆栈上有本机帧时-当Java代码调用本机代码(JNI)时然后调用Java,当在
synchronized块或方法中时。在这种情况下,阻止虚拟线程将阻止承载虚拟线程的物理线程。一旦本机调用完成或监视器释放(退出synchronized块/方法),线程将被取消锁定
如果您有一个由
synchronized保护的通用I/O操作,请使用ReentrantLock替换监视器,以便在我们修复监视器固定之前让您的应用程序充分受益于绝缘线束的可伸缩性提升(或者,更好的是,如果可以的话,使用更高性能的StampedLock)