前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >快速了解重入锁实现原理

快速了解重入锁实现原理

作者头像
AI码师
发布2022-09-19 11:48:58
2750
发布2022-09-19 11:48:58
举报

什么是重入锁?

从字面意思理解就是“重新进入同步区域”,同一个线程,多次获取一把锁。

哪些锁支持重入呢?

Synchronized

synchronized是支持重入的,它是隐式的获取去重入锁,如下:

代码语言:javascript
复制
package com.ams.thread.lesson7;

import lombok.extern.slf4j.Slf4j;

/**
 * 关注微信公众号"AI码师"获取项目源码及2021面试题一套
 *
 * @author: AI码师
 * Date: 2021/12/30 6:08 上午
 * Description:
 */
@Slf4j
public class Example17 {
    public static void main(String[] args) {
        new Thread(new Thread1()).start();
    }

    static class Thread1 implements Runnable {

        @Override
        public void run() {
            synchronized (Example17.class) {
                synchronized (Example17.class) {
                    log.info("成功获取重入锁");
                }
            }
        }
    }
}

ReentrantLock

reentrantLock 也是支持重入的,不过他是需要显示的获取重入锁,并且它还可以支持非公平和公平锁:

代码语言:javascript
复制
package com.ams.thread.lesson7;

import lombok.extern.slf4j.Slf4j;

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

/**
 * 关注微信公众号"AI码师"获取项目源码及2021面试题一套
 *
 * @author: AI码师
 * Date: 2021/12/30 6:08 上午
 * Description:
 */
@Slf4j
public class Example18 {
    private static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        new Thread(new Thread1()).start();
    }

    static class Thread1 implements Runnable {

        @Override
        public void run() {
            lock.lock();
            lock.lock();
            log.info("获取到重入锁");
            lock.unlock();
            lock.unlock();

        }
    }
}

实现重入锁,关键点是什么?

重复进入

当前线程本次获取锁之后,下次在获取改锁的时候,判断为当前线程则直接进入,不阻塞。

锁的释放

如果线程进入了n次,那么它只有释放n次之后,才是真正的释放锁。

我们前面实现了一个锁,但是它是不支持重入的,我们现在给他进行改造:

手写一个重入锁

改造的关键点:

  • 获取锁时,需要判断当前锁是否被占用,如果没有被占用则获取,否则判断是否是当前线程占用,如果是则计数加1,否则等待锁释放。
  • 锁的释放,锁释放时要判断是否是当前线程,如果是,则计数减1,直到为0 ,才是真正释放锁

上代码

代码语言:javascript
复制
package com.ams.thread.lesson7;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * 关注微信公众号"AI码师"获取项目源码及2021面试题一套
 * 实现重入锁
 *
 * @author: AI码师
 * Date: 2021/12/30 6:09 上午
 * Description:
 */
public class ReentrantMutexLock implements Lock {
    private final LockAQS lockAQS = new LockAQS();

    @Override
    public void lock() {
        lockAQS.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        lockAQS.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        return lockAQS.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return lockAQS.tryAcquireNanos(1, unit.toNanos(time));
    }

    @Override
    public void unlock() {
        lockAQS.release(1);
    }

    @Override
    public Condition newCondition() {
        return lockAQS.newCondition();
    }

    private static class LockAQS extends AbstractQueuedSynchronizer {
        /**
         * 重新获取锁的逻辑,只有当前同步状态为0,才允许获取成功
         *
         * @param arg
         * @return
         */
        @Override
        protected boolean tryAcquire(int arg) {
            // 通过cas机制去设置新值,如果当前锁没有被占用,则期望值肯定为0 并且新值为 获取的资源数量
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
                // 当前则为重新获取同一把锁,则进行计数加1
            } else if (getExclusiveOwnerThread() == Thread.currentThread()) {
                return compareAndSetState(getState(), getState() + 1);
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int arg) {
            // 首先判断当前state 如果当前状态值不是大于0 则释放非法
            if (getState() == 0) {
                return false;
            }
            //  判断当前获取锁的线程是否是当前线程 如果是的,则可以释放锁,否则抛出异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            // 计数减去当前获取的资源
            setState(getState() - 1);
            // 计数为0,则代表全部释放,将独占线程标识置空
            if (getState() == 0) {
                setExclusiveOwnerThread(null);
            }
            return true;
        }

        Condition newCondition() {
            return new ConditionObject();
        }
    }

}

代码语言:javascript
复制
package com.ams.thread.lesson7;

import cn.hutool.core.thread.ThreadUtil;
import com.ams.thread.lesson5.MyMutexLock;
import lombok.extern.slf4j.Slf4j;

/**
 * 关注微信公众号"AI码师"获取项目源码及2021面试题一套
 * 验证重入锁
 * @author: AI码师
 * Date: 2021/12/30 6:08 上午
 * Description:
 */
@Slf4j
public class Example19 {
private static ReentrantMutexLock reentrantMutexLock = new ReentrantMutexLock();

    public static void main(String[] args) {
        new Thread(new Thread1()).start();
        new Thread(new Thread2()).start();
    }
    static class Thread1 implements Runnable {

        @Override
        public void run() {
            reentrantMutexLock.lock();
            ThreadUtil.sleep(1000);
            log.info("Thread1 获取到锁");
            ThreadUtil.sleep(1000);
            reentrantMutexLock.lock();
            ThreadUtil.sleep(1000);
            log.info("Thread1 再次获取到锁");
            ThreadUtil.sleep(1000);
            reentrantMutexLock.unlock();
            ThreadUtil.sleep(1000);
            log.info("Thread1 释放锁");
            reentrantMutexLock.unlock();
            ThreadUtil.sleep(1000);
            log.info("Thread1 释放所有锁");
        }
    }
    static class Thread2 implements Runnable {

        @Override
        public void run() {
            reentrantMutexLock.lock();
            ThreadUtil.sleep(1000);
            log.info("Thread2 获取到锁");
            reentrantMutexLock.lock();
            ThreadUtil.sleep(1000);
            log.info("Thread2 再次获取到锁");
            ThreadUtil.sleep(5000);
            ThreadUtil.sleep(1000);
            reentrantMutexLock.unlock();
            log.info("Thread2 释放锁");
            reentrantMutexLock.unlock();
            ThreadUtil.sleep(1000);
            log.info("Thread2 释放所有锁");
        }
    }
}

通过执行结果可以看出:输出结果是按照我们期望执行的。需要注意的是,使用该锁在调用unlock方法时,锁被立即释放,并不会等待当前方法块执行完,和synchronized同步块,所以严禁在unlock方法后继续写需要进行同步逻辑

关注公众号领取2022最新面试题一套和项目源码

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-01-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 乐哥聊编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是重入锁?
  • 哪些锁支持重入呢?
    • Synchronized
      • ReentrantLock
      • 实现重入锁,关键点是什么?
        • 重复进入
          • 锁的释放
          • 手写一个重入锁
            • 改造的关键点:
              • 上代码
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档