ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。

此外,它还提供了在激烈争用情况下更佳的性能。

ReentrantLock与synchronized的不同点

(1)ReentrantLock功能性方面更全面,比如时间锁等候,可中断锁等候,锁投票等,因此更有扩展性。在多个条件变量和高度竞争锁的地方,

用ReentrantLock更合适,ReentrantLock还提供了Condition,对线程的等待和唤醒等操作更加灵活,一个ReentrantLock可以有多个Condition

实例,所以更有扩展性。

(2)ReentrantLock 的性能比synchronized会好点。

(3)ReentrantLock提供了可轮询的锁请求,他可以尝试的去取得锁,如果取得成功则继续处理,取得不成功,可以等下次运行的时候处理,所以不

容易产生死锁,而synchronized则一旦进入锁请求要么成功,要么一直阻塞,所以更容易产生死锁。

二。作用

1.实现可轮询的锁请求 

  在内部锁中,死锁是致命的,唯一的恢复方法是重新启动程序,唯一的预防方法是在构建程序时不要出错。而可轮询的锁获取模式具有更完善的错误

恢复机制,可以规避死锁的发生。

  如果你不能获得所有需要的锁,那么使用可轮询的获取方式使你能够重新拿到控制权,它会释放你已经获得的这些锁,然后再重新尝试。可轮询的锁

获取模式,由tryLock()方法实现。此方法仅在调用时锁为空闲状态才获取该锁。如果锁可用,则获取锁,并立即返回值true。如果锁不可用,则此方法将

立即返回值false。此方法的典型使用语句如下

    Lock lock = ...;
if (lock.tryLock()) {
try {
// manipulate protected state
} finally {
lock.unlock();
}
} else {
// perform alternative actions
}

2. 实现可定时的锁请求 

synchronized进入锁请求要么成功,要么一直阻塞,所以更容易产生死锁。tryLock(long, TimeUnit)如果在期待的时间内没能获得锁,程序返回false。

 package concurrent;

 import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock; public class AttemptLocking { private ReentrantLock lock = new ReentrantLock(); public void untimed(){
boolean captured = lock.tryLock();
try{
System.out.println("tryLock(): " + captured);
}finally{
if(captured){
lock.unlock();
}
}
} public void timed(){
boolean captured = false; try {
captured = lock.tryLock(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} try{
System.out.println("tryLock(2, TimeUnit.SECONDS): " + captured);
}finally{
if(captured){
lock.unlock();
}
}
} public static void main(String[] args) throws InterruptedException {
final AttemptLocking attemp = new AttemptLocking();
attemp.untimed();
attemp.timed(); new Thread(){
{setDaemon(true);}
public void run(){
attemp.lock.lock(); //该线程一直没释放锁
System.out.println("lock");
}
}.start();
Thread.sleep(3600);
Thread.yield(); attemp.untimed();
attemp.timed(); } }

结果:

tryLock(): true
tryLock(2, TimeUnit.SECONDS): true
lock
tryLock(): false
tryLock(2, TimeUnit.SECONDS): false

3.实现可中断的锁获取请求 

可中断的锁获取操作允许在可取消的活动中使用。lockInterruptibly()方法能够使你获得锁的时候响应中断。
public class TestLockInterruptibly {

    public static void testLock() throws Exception {
final Lock lock = new ReentrantLock();
//锁已被获取
lock.lock(); Thread t1 = new Thread(()->{
//不能获取,不会响应中断
lock.lock();
//下面的语句没有执行机会
System.out.println(Thread.currentThread().getName() + " lock不会响应中断, 一直等到锁");
}); t1.start(); t1.interrupt();
} public static void testLockInterruptibly() throws Exception{
final Lock lock = new ReentrantLock();
//锁已被获取
lock.lock(); Thread t1 = new Thread(()->{
try {
//不能获取,可以响应中断
lock.lockInterruptibly();
} catch (InterruptedException e) {
System.out.println("InterruptedException : " + Thread.currentThread().getName() + " interrupted.");
}
}); t1.start(); t1.interrupt();
} public static void main(String[] args) throws Exception {
testLock();
// testLockInterruptibly();
}
}

三、公平锁、非公平锁

默认是非公平锁

    /**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}

有一个state变量,初始值为0,假设当前线程为A,每当A获取一次锁,status++. 释放一次,status--.锁会记录当前持有的线程。
当A线程拥有锁的时候,status>0. B线程尝试获取锁的时候会对这个status有一个CAS(0,1)的操作,尝试几次失败后就挂起线程,进入一个等待队列。
如果A线程恰好释放,--status==0, A线程会去唤醒等待队列中第一个线程,即刚刚进入等待队列的B线程,B线程被唤醒之后回去检查这个status的值,尝试CAS(0,1),而如果这时恰好C线程也尝试去争抢这把锁

非公平锁实现:
C直接尝试对这个status CAS(0,1)操作,并成功改变了status的值,B线程获取锁失败,再次挂起,这就是非公平锁,B在C之前尝试获取锁,而最终是C抢到了锁。
公平锁:
C发现有线程在等待队列,直接将自己进入等待队列并挂起,B获取锁

非公平锁性能高于公平锁性能的原因:在恢复一个被挂起的线程与该线程真正运行之间存在着严重的延迟。

当持有锁的时间相对较长或者请求锁的平均时间间隔较长,应该使用公平锁。在这些情况下,插队带来的吞吐量提升(当锁处于可用状态时,线程却还处于被唤醒的过程中)可能不会出现。

 
四、选择

一般来说,除非您对 Lock 的某个高级特性有明确的需要,或者有明确的证据(而不是仅仅是怀疑)表明在特定情况下,同步已经成为

可伸缩性的瓶颈,否则还是应当继续使用 synchronized。

  • 在使用 synchronized 的时候,不能忘记释放锁;在退出 synchronized 块时,JVM 会为您做这件事。

  • 用 synchronized 管理锁定请求和释放时,JVM 在生成线程转储时能够包括锁定信息。这些对调试非常有价值,因为它们能标识死锁或

者其他异常行为的来源。 Lock 类只是普通的类,JVM 不知道具体哪个线程拥有 Lock 对象。

ReentrantLock VS synchronized的更多相关文章

  1. Java中的ReentrantLock和synchronized两种锁定机制的对比

    问题:多个访问线程将需要写入到文件中的数据先保存到一个队列里面,然后由专门的 写出线程负责从队列中取出数据并写入到文件中. http://blog.csdn.net/top_code/article/ ...

  2. ReentrantLock和synchronized两种锁定机制

    ReentrantLock和synchronized两种锁定机制 >>应用synchronized同步锁 把代码块声明为 synchronized,使得该代码具有 原子性(atomicit ...

  3. java多线程之:Java中的ReentrantLock和synchronized两种锁定机制的对比 (转载)

    原文:http://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html 多线程和并发性并不是什么新内容,但是 Java 语言设计中的创新之 ...

  4. ReentrantLock与synchronized的差别

    总的来说,lock更加灵活. 主要同样点:Lock能完毕synchronized所实现的全部功能 不同: 1.ReentrantLock功能性方面更全面,比方时间锁等候,可中断锁等候,锁投票等,因此更 ...

  5. Java中的ReentrantLock和synchronized两种锁机制的对比

    原文:http://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html 多线程和并发性并不是什么新内容,但是 Java 语言设计中的创新之 ...

  6. Java中的ReentrantLock和synchronized两种锁定

    原文:http://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html 多线程和并发性并不是什么新内容,但是 Java 语言设计中的创新之 ...

  7. Java中的ReentrantLock和synchronized两种锁定机制

    原文:http://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html 多线程和并发性并不是什么新内容,但是 Java 语言设计中的创新之 ...

  8. ReentrantLock与synchronized

    1.ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候 线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O ...

  9. Java ReentrantLock和synchronized两种锁定机制的对比

    多线程和并发性并不是什么新内容,但是 Java 语言设计中的创新之一就是,它是第一个直接把跨平台线程模型和正规的内存模型集成到语言中的主流语言.核心类库包含一个 Thread 类,可以用它来构建.启动 ...

  10. ReentrantLock和synchronized区别和联系?

    相同:ReentrantLock提供了synchronized类似的功能和内存语义,都是可重入锁. 不同: 1.ReentrantLock功能性方面更全面,比如时间锁等候,可中断锁等候,锁投票等,因此 ...

随机推荐

  1. 移动端H5实现图片上传

    移动端H5实现图片上传 https://segmentfault.com/a/1190000010034177

  2. 【应用篇】Activiti外置表单实例demo(四)

    在这里我想说的外置表单.是说我们将我们自己的jsp(.form,.html)等页面上传到工作流的数据库中,当任务运行到当前结点时.给我们像前台发送绑定好的表单. 此处是给表单绑定表单的过程 water ...

  3. Lua顺序 执行顺序

    1.4.2. Lua顺序 Nginx下Lua处理阶段与使用范围: init_by_lua http set_by_lua server, server if, location, location i ...

  4. iOS开发多线程篇 09 —NSOperation简单介绍

    iOS开发多线程篇—NSOperation简单介绍 一.NSOperation简介 1.简单说明 NSOperation的作⽤:配合使用NSOperation和NSOperationQueue也能实现 ...

  5. std::copy ( myvector.begin(), myvector.end(), out_it )

    在实际生产环境中,不能进行调试,所以程序通常需要编译一个DEBUG版本来辅助我们找出问题所在,编译这样的DEBUG版本最常用的手段就是在关键处输出我们关心一些变量的值到屏幕. 如果输出的简单的变量值, ...

  6. shell脚本中处理 路径中中文和空格方法

    OLDIFS=$IFS IFS=$'\n' #存放路径的变量在引用时都使用双引号括起来 "$VAR_PATH" #路径拼接时后续的旧不需要加双引号了"$VAR_PATH& ...

  7. Springboot集成权限管理框架apache shiro

    一.名词解释 网上一大堆 二.pom依赖 <dependency> <groupId>org.apache.shiro</groupId> <artifact ...

  8. 【转】Web前端开发:为何选择MVVM而非MVC

    在Web中充斥着所谓的MVC框架,而在我看来,因为一些关键性的技术原因,MVC在Web前端开发中根本无法使用(对的,是无法,而不是不该) 在Web中充斥着所谓的MVC框架,而在我看来,因为一些关键性的 ...

  9. Web 层由 Web,Web-MVC,Web-Socket 和 Web-Portlet 组成

    Web 层由 Web,Web-MVC,Web-Socket 和 Web-Portlet 组成,它们的细节如下: Web 模块提供面向web的基本功能和面向web的应用上下文,比如多部分(multipa ...

  10. Java报表生成技术

    报表是以格式化的形式输出数据,并对数据进行分组.汇总.计算等操作.通过报表.图表或者嵌入图片图像等形式来丰富数据的显示. 报表生成的步骤: 后台数据抽取(DAO层): 数据项逻辑运算(业务逻辑层): ...