Java 多线程 重入锁

  作为关键字synchronized的替代品(或者说是增强版),重入锁是synchronized的功能扩展。在JDK 1.5的早期版本中,重入锁的性能远远好于synchronized,但从JDK 1.6开始,JDK优化了synchronized,使两者性能差距不大。重入锁使用java.util.concurrent.locks.ReentrantLock类来实现。

  使用重入锁可以指定何时加锁和何时释放锁,对逻辑控制的灵活性远远好于synchronized,退出临界区时必须释放锁。之所以称为重入锁,是因为一个线程可以连续获得这种锁。

  简单的重入锁使用例子如下:

  

  修改上述代码第7-12行:

  

  在这种情况下,一个线程连续两次获得同一把锁。如果不允许该操作,那么同一个线程在第2次请求获得锁时,会和自己产生死锁,程序卡死在第2次申请锁的过程中。如果同一个线程多次获得锁,那么释放锁的次数与其相同。如果释放锁的次数多,那么会有java.lang.IllegalMonitorStateException异常;反之,如果释放锁的次数少了,那么该线程依然持有这个锁。

  除了使用上的灵活性之外,重入锁还提供了一些高级功能:

  1 中断响应

  对于synchronized来说,如果一个线程在等待锁,那么结果只有两种可能:要么获得这把锁继续执行,要么保持等待。而使用重入锁可以提供另外一种可能:线程可以被中断。也就是说,线程在等待锁的过程中,程序可以根据需要取消对锁的请求。例如,小王与小明一起去打球,如果小王等了10分钟,小明还没到,接到小明的电话了解到由于突发情况不能如约了,那么小王就扫兴地打道回府了。对于线程而言,如果一个线程正在等待锁,那么它可以收到一个通知,被告知无需再等待,可以停止工作了。这种情况对于处理死锁是有一定帮助的。

  2 锁申请等待限时

  除了等待外部通知之外,避免死锁还有另外一种方法:限时等待。继续上面的例子,如果小王等了半个小时,小明还不来,那么小王就扫兴离去了。对于线程而言,如果给定一个等待时间,让线程自动放弃,那么可以避免线程持续等待。

  3 公平锁

  在大多数情况下,锁的申请都是非公平的。也就是说,线程1首先申请了锁A,接着线程2也申请了锁A,那么当锁A可用时,哪个线程获得锁是不一定的,系统会从锁A的等待队列中随机选择一个,不能保证公平性。如同买票不排队,大家乱哄哄地围在售票窗口前,售票员忙得焦头烂额,顾不得谁先谁后,随便找个人出票就完事了。而公平锁保证先到先得,不会产生饥饿现象。只要排好队,最终可以获得资源。synchronized产生的锁是非公平的,而重入锁可以设置是否公平。重入锁要求系统维护一个有序队列,实现成本比较高,性能较低。所以,默认情况下的锁是非公平的。

  ReentrantLock类常见方法如下:

  1 public ReentrantLock(boolean fair)

  当参数fair是true时,表示该重入锁是公平的。

  2 lock()

  获取锁,如果锁已被占用,则等待。

  3 lockInterruptibly()

  获取锁,但优先响应中断。

  4 tryLock()

  尝试获取锁,如果成功,返回true;否则,返回false。该方法不等待,立即返回。

  5 tryLock(long time, TimeUnit unit)

  在给定时间内尝试获取锁。

  6 unlock()

  释放锁。

  重入锁的实现包含3个要素:

  1 原子状态

  原子状态使用CAS操作来存储当前锁的状态,判断锁是否已被其他线程持有。

  2 等待队列

  所有没有获取到锁的线程会进入等待队列进行等待。当有线程释放锁后,系统就从等待队列中唤醒一个线程,继续工作。

  3 阻塞原语park()和unpark()

  挂起和恢复线程,没有获取到锁的线程会被挂起。

  

  参考资料

  《实战Java高并发程序设计》 P71-80

原文地址:https://www.cnblogs.com/WJQ2017/p/8276326.html

时间: 2024-10-03 15:20:48

Java 多线程 重入锁的相关文章

java多线程---重入锁ReentrantLock

1.定义 重入锁ReentrantLock,支持重入的锁,表示一个线程对资源的重复加锁. 2.底层实现 每个锁关联一个线程持有者和计数器,当计数器为0时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法:成功后,JVM会记下锁的持有线程,并且将计数器置为1:此时其它线程请求该锁,则必须等待:而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增:当线程退出同步代码块时,计数器会递减,如果计数器为0,则释放该锁. 3.使用样例 eg: import java

JAVA多线程重入锁ReentrantLock应用

package concurrent; import java.util.concurrent.*; import java.util.concurrent.locks.ReentrantLock; /** * @Auther:zhl * @Date:2019/7/13 * @Description: 并发测试,重入锁ReentrantLock解决并发问题 */ public class ConcurrentSample { //并发线程数量 private static int users =

java 可重入锁ReentrantLock的介绍

一个小例子帮助理解 话说从前有一个村子,在这个村子中有一口水井,家家户户都需要到这口井里打水喝.由于井水有限,大家只能依次打水.为了实现家家有水喝,户户有水用的目标,村长绞尽脑汁,最终想出了一个比较合理的方案. 首先,在水井边上安排一个看井人,负责维持秩序. 然后,打水时,以家庭为单位,哪个家庭任何人先到井边,就可以先打水,而且如果一个家庭占到了打水权,其家人这时候过来打水不用排队.而那些没有抢占到打水权的人,一个一个挨着在井边排成一队,先到的排在前面. 最后,打水的人打完水以后就告诉看井人,看

Java可重入锁

锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) .这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类型却很少被提及.本系列文章将分析JAVA下常见的锁名称以及特性,为大家答疑解惑. 四.可重入锁: 本文里面讲的是广义上的可重入锁,而不是单指JAVA下的ReentrantLock. 可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响. 在JAV

多线程——重入锁

当某个线程请求一个由其它线程持有的锁时,该线程就会进入等待或者阻塞状态,一直到持有锁的线程释放锁,然后再去竞争获取锁.然而,内置锁(即Synchronized修饰的锁对象)是可重入的,因此如果某个线程试图获得一个已经由它自己持有的锁,那么它会成功获得此锁,这就是所谓的重入锁,也可以理解为锁的重入. "重入",意味着获取锁的操作的粒度是"线程",而不是"调用","调用"只是获取锁的途径. "重入"的一种实现方

[图解Java]ReentrantLock重入锁

图解ReentrantLock 0. demo 我先给出一个demo, 这样大家就可以根据我给的这段代码, 边调试边看源码了. 还是那句话: 注意"My" , 我把ReentrantLock类 改名为了 "MyReentrantLock"类 , "Lock"类 改名为了"MyLock"类. 大家粘贴我的代码的时候, 把相应的"My"都去掉就好了, 否则会编译报错哦. import java.util.Sca

可重入锁的机制

1.听故事把知识掌握了 在一个村子里面,有一口井水,水质非常的好,村民们都想打井里的水.这井只有一口,村里的人那么多,所以得出个打水的规则才行.村长绞尽脑汁,最终想出了一个比较合理的方案,咱们来仔细的看看聪明的村长大人的智慧. 井边安排一个看井人,维护打水的秩序. 打水时,以家庭为单位,哪个家庭任何人先到井边,就可以先打水,而且如果一个家庭占到了打水权,其家人这时候过来打水不用排队.而那些没有抢占到打水权的人,一个一个挨着在井边排成一队,先到的排在前面.打水示意图如下 : 是不是感觉很和谐,如果

Java多线程可重入锁例子解析

"可重入锁"的概念是:自己可以再次获得自己的内部锁.比如有一条线程获得了某个对象的锁,此时这个对象还没有释放,当其再次想获得这个对象的锁的时候还是可以获得的,如果不可锁重入的话,就会造成死锁. class sysTest{ synchronized void test1(String str){ System.out.println(str+"1"); test2(str); System.out.println("end" + str); }

java 多线程-可重入锁

可重入锁:锁可以连续使用计数器+判断进入的线程是不是已经锁定的线程,如果是那就不用等待,直接使用 public class my { public static void main(String[]args) { my m=new my(); m.test(); } public void test() { synchronized(this)//第一次获得锁 { while(true) { synchronized(this)//第二次获得锁,假如没有可重入锁,将会造成死锁 { System.