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. elasticsearch 索引设置

    索引设置 你可以通过很多种方式来自定义索引行为,你可以阅读Index Modules reference documentation,但是: 提示: Elasticsearch 提供了优化好的默认配置 ...

  2. css3中-moz、-ms、-webkit,-o分别代表的意思

    这种方式在业界上统称:识别码.前缀 //-ms代表[ie]内核识别码 //-moz代表火狐[firefox]内核识别码 //-webkit代表谷歌[chrome]/苹果[safari]内核识别码 // ...

  3. Atitit.js this错误指向window的解决方案

    Atitit.js this错误指向window的解决方案 1.1. 出现地点and解决之道1 1.2. call,apply和bind这三个方法2 1.2.1. Function.prototype ...

  4. 树莓派、 Arduino 、传统单片机开发板该如何选择?

    几十年前的电子爱好者,最喜欢的就是电烙铁.面包板和收音机:十几年前,出现了单片机,于是玩具就成了电烙铁.面包板和单片机:到了2015年,贴片技术的不断普及,让面包板不再那么有用武之地,经济的发展也让现 ...

  5. go开启多进程——拆分多个进程同时处理(分而治之)

    使用了goroutine实现了多线程,使用chan来控制多线程. runtime.GOMAXPROCS(3)来设置最大的原生线程. runtime.Gosched() 显式地让出CPU时间给其他gor ...

  6. 在eclipse中使用枚举简单类型enum

    在JAVA中终于可以使用枚举类型了,就像在C或C++使用的简单枚举. 首先就在eclipse中试试它吧. 没想到,却报了错误.我装了jdk1.5(5.0),也在eclipse中设置了,怎么会不认识en ...

  7. VBA 字符串操作

    Trim(string) 去掉string左右两端空白 Ltrim(string) 去掉string左端空白 Rtrim(string) 去掉string右端空白 Len(string) 计算stri ...

  8. js 数组容易弄混的那些方法

    js数组中 我们很多数组的方法都知道其中的含义 但是每一次用的都不是很顺手 下边我给大家写了一个小小的demo 来理解那些 近似”双胞胎“的东西 // splice方法 var a=[1,2,3,4, ...

  9. MySQL的语法高级之SELECT

    1.语法:select 字段列表 from  子句 [where 子句][group by 子句][ order by 子句][having 子句][limit 子句]; 注解: 1.where子句对 ...

  10. php的下载

    前言:你的坚持,终将美好! 今天给大家说一下,php的下载,由于php的下载本身就是一个压缩包,解压缩后即可使用.所以,解压缩的过程不再赘述. 第一步:在浏览器的地址栏输入:http://www.ph ...