可重入锁与不可重入锁的理解

╰半夏微凉° 2021-09-30 06:46 517阅读 0赞

JDK中可以使用ReentrantLock类来实现可重入锁,其功能更加丰富,使用起来更为灵活,也更适合复杂的并发场景。下面来自己实现以下可重入锁。原理一样。

  1. ReentrantLock
    - 不可重入锁
    不可重入锁,锁不可延续使用。

    public class LockTest1 {

    1. Lock lock=new Lock();
    2. public void a() throws InterruptedException {
    3. lock.lock();
    4. doSomething();
    5. lock.unlock();
    6. }
    7. //不可重入锁
    8. public void doSomething() throws InterruptedException {
    9. lock.lock();
    10. //
    11. lock.unlock();
    12. }
    13. public static void main(String[] args) throws InterruptedException {
    14. LockTest1 test1=new LockTest1();
    15. test1.a();
    16. test1.doSomething();
    17. }

    }
    class Lock{

    1. //是否占用
    2. private boolean isLock=false;
    3. //使用锁
    4. public synchronized void lock() throws InterruptedException {
    5. while (isLock){
    6. wait();
    7. }
    8. isLock=true;
    9. }
    10. //释放锁
    11. public synchronized void unlock(){
    12. isLock=false;
    13. notify();
    14. }

    }

如此代码中,方法a()首先得到得到锁,然后调用方法doSomething(),方法doSomething()中有lock.lock(),也需要上锁,然后就等待a()方法执行完,方法a()必须执行完doSomething()方法才会运行下一条语句unlock()方法释放锁,同时方法doSomething()等方法a()运行完释放锁,就形成了死循环。永远不会停止,永远等待。上述就是不可重入锁。

- 可重入锁
锁可以延续使用

  1. public class LockTest2 {
  2. ReLock lock=new ReLock();
  3. public void a() throws InterruptedException {
  4. System.out.println(Thread.currentThread().getName());
  5. lock.lock();
  6. System.out.println(lock.getHoldCount());
  7. doSomething();
  8. lock.unlock();
  9. System.out.println(lock.getHoldCount());
  10. }
  11. //不可重入锁
  12. public void doSomething() throws InterruptedException {
  13. System.out.println(Thread.currentThread().getName());
  14. lock.lock();
  15. System.out.println(lock.getHoldCount());
  16. //
  17. lock.unlock();
  18. System.out.println(lock.getHoldCount());
  19. }
  20. public static void main(String[] args) throws InterruptedException {
  21. LockTest2 test1=new LockTest2();
  22. test1.a();
  23. Thread.sleep(1000);
  24. System.out.println(test1.lock.getHoldCount());
  25. }
  26. }
  27. class ReLock{
  28. //是否占用
  29. private boolean isLock=false;
  30. Thread lockedBy=null;//储存线程//如果自身就不等了
  31. private int holdCount=0;//计数器
  32. public int getHoldCount() {
  33. return holdCount;
  34. }
  35. //使用锁
  36. public synchronized void lock() throws InterruptedException {
  37. Thread thread = Thread.currentThread();//加锁时识别当前线程
  38. System.out.println(Thread.currentThread().getName());
  39. while (isLock &&lockedBy!=thread){//不是自己的线程就等待
  40. wait();
  41. }
  42. isLock=true;
  43. lockedBy=thread;
  44. holdCount++;
  45. }
  46. //释放锁
  47. public synchronized void unlock(){
  48. System.out.println(Thread.currentThread().getName());
  49. if(Thread.currentThread()==lockedBy){//当前线程等于自身的时候释放
  50. holdCount--;//计数器减一
  51. if(holdCount==0) {
  52. isLock = false;
  53. notify();
  54. lockedBy=null;
  55. }
  56. }
  57. }
  58. }

运行结果:
在这里插入图片描述
可重入锁中引入了计数器,上锁就会加一,解除锁减一,直到计数器为零,完全释放锁。

  • 上锁的时候先识别当前的线程,一开始isLock为false,则不进行while循环,下一步isLock改为true,计数器加一,此时就完成了第一次上锁;等第二次上锁的时候,isLock已经是true了,此时判断线程是不是当前线程,是当前线程则继续上锁,计数器加一,不用等待。后面的同理。在此例子中通过输出当前线程名,都是main线程,线程相同。所以可以完成加锁减锁。
  • 释放锁的时候同理判断是否是当前锁,是则计数器减一,释放锁,并通过notify()方法唤醒被wait()等待的线程,将lockBy置为空。

它和不可重入锁的设计不同之处:

  • 不可重入锁:只判断这个锁有没有被锁上,只要被锁上申请锁的线程都会被要求等待。实现简单
  • 可重入锁:不仅判断锁有没有被锁上,还会判断锁是谁锁上的,当就是自己锁上的时候,那么他依旧可以再次访问临界资源,并把加锁次数加一。
  • 设计了加锁次数,以在解锁的时候,可以确保所有加锁的过程都解锁了,其他线程才能访问。不然没有加锁的参考值,也就不知道什么时候解锁?解锁多少次?才能保证本线程已经访问完临界资源了可以唤醒其他线程访问了。实现相对复杂。

发表评论

表情:
评论列表 (有 0 条评论,517人围观)

还没有评论,来说两句吧...

相关阅读

    相关

    可重入锁与非可重入锁 可重入锁又称递归锁,是指同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提是锁对象得是同一个对象),不会因为之前已经获