您当前的位置: 首页 > 

Dongguo丶

暂无认证

  • 2浏览

    0关注

    472博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

可重入锁(递归锁)

Dongguo丶 发布时间:2021-09-16 09:02:16 ,浏览量:2

可重入锁

synchronized 和Lock都是可重入锁,也叫递归锁,即一个线程可以重复获取同一把锁

是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象),不会因为之前已经获取过还没释放而阻塞。

Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。

synchronized 是隐式锁,Lock是显式锁

即synchronized 加锁解锁自动完成

Lock加锁解锁要手动完成

synchronized synchronized 可重入锁(同步代码块)
package com.dongguo.sync;

/**
 * @author Dongguo
 * @date 2021/8/24 0024-13:58
 * @description:synchronized 可重入锁
 */
public class SyncLockDemo {
    public static void main(String[] args) {
        Object object = new Object();
        new Thread(() -> {
            synchronized (object) {
                System.out.println(Thread.currentThread().getName() + "-外层");
                synchronized (object) {
                    System.out.println(Thread.currentThread().getName() + "-中层");
                    synchronized (object) {
                        System.out.println(Thread.currentThread().getName() + "-内层");
                    }
                }
            }
        }, "ThreadA").start();
    }
}
运行结果:
ThreadA-外层
ThreadA-中层
ThreadA-内层
synchronized 递归锁(同步方法)
package com.dongguo.sync;

/**
 * @author Dongguo
 * @date 2021/8/24 0024-13:58
 * @description: synchronized 可重入锁
 */
public class SyncLockDemo {
    public static void main(String[] args) {
        new SyncLockDemo().add();
    }
    public long number = 0;
    //递归
    public synchronized void add(){
        number++;
        System.out.println(number);
        add();//自己调自己
    }
}

image-20210903210953346

Synchronized的重入的实现原理

每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。

当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。

在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么 Java 虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。

当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。

ReentrantLock

image-20210913150747857

ReentrantLock可重入锁

ReentrantLock相对于 synchronized 它具备如下特点 可中断 可以设置超时时间 可以设置为公平锁 支持多个条件变量 与 synchronized 一样,都支持可重入

package com.dongguo.sync;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Dongguo
 * @date 2021/8/24 0024-13:58
 * @description: Lock 可重入锁
 */
public class SyncLockDemo {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        new Thread(() -> {
            try {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + "-外层");

                try {
                    lock.lock();
                    System.out.println(Thread.currentThread().getName() + "-中层");
                    
                    try {
                        lock.lock();
                        System.out.println(Thread.currentThread().getName() + "-内层");
                    } finally {
                        lock.unlock();
                    }
                } finally {
                    lock.unlock();
                }
            } finally {
                lock.unlock();
            }
        }, "ThreadA").start();
    }
}
运行结果:
ThreadA-外层
ThreadA-中层
ThreadA-内层

注意:加锁几次就要解锁几次

package com.dongguo.sync;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Dongguo
 * @date 2021/8/24 0024-13:58
 * @description:
 */
public class SyncLockDemo1 {

    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        new Thread(() -> {
            try {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + "-外层");

                try {
                    lock.lock();
                    System.out.println(Thread.currentThread().getName() + "-中层");

                    try {
                        lock.lock();
                        System.out.println(Thread.currentThread().getName() + "-内层");
                    } finally {
                        lock.unlock();
                    }
                } finally {
                    //lock.unlock(); //这里故意注释,实现加锁次数和释放次数不一样
                    //由于加锁次数和释放次数不一样,第二个线程始终无法获取到锁,导致一直在等待。
                }
            } finally {
                lock.unlock();
            }
        }, "t1");
        new Thread(() -> {
            try {
                lock.lock();
                System.out.println("t2获得lock锁");
            } finally {
                lock.unlock();
            }
        }, "t2");
    }
}

image-20210906095443516

由于t1获取锁与释放锁的次数不同,t2无法获得lock锁

ReentrantLock递归锁
package com.dongguo.sync;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Dongguo
 * @date 2021/8/24 0024-13:58
 * @description:
 */
public class SyncLockDemo {
    Lock lock = new ReentrantLock();
    public static void main(String[] args) {
        new SyncLockDemo().add();
    }
    public long number = 0;

    //递归
    public void add(){
        try {
            lock.lock();
            number++;
            System.out.println(number);
            add();//自己调自己
        } finally {
            lock.unlock();
        }
    }
}

运行结果:

image-20210903211958954

关注
打赏
1638062488
查看更多评论
立即登录/注册

微信扫码登录

0.0419s