java重入锁

一、重入锁理解

  • 某线程获取锁后,可以再次获取锁
  • synchronized 和 ReentrantLock 都是可重入锁
  • 可重入锁解决同一线程中内外层方法调用死锁的问题

二、可重入锁示例

1、synchronized 可重入锁演示

    /**
     * synchronized 重入锁示例
     */
    private static void reLockSyncDemo(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (this){
                    System.out.println("第1次获取锁,当前锁是:" + this);
                    int index=0;
                    while(true){
                        synchronized (this){
                            System.out.println("第" + (++index) + "次获取重入锁,当前锁是:" + this);
                        }
                        if (index == 5) {
                            break;
                        }
                    }
                }
            }
        }).start();
    }

在这里插入图片描述

2、ReentronLock 可重入锁演示

 		/**
     * ReentrantLock 重入锁示例
     */
    private static void reentrantLockDemo(){
        ReentrantLock rl = new ReentrantLock();
        new Thread(new Runnable() {
            @Override
            public void run() {
                rl.lock();
                try {
                    System.out.println("第1次获取锁,当前锁是:" + rl);
                    int index = 0;
                    while (true) {
                        rl.lock();
                        try {
                            System.out.println("第" + (++index) + "次获取重入锁,当前锁是:" + rl);
                            if (index == 5) {
                                break;
                            }
                        } finally {
                            rl.unlock();
                        }
                    }
                }finally{
                    rl.unlock();
                }
            }
        }).start();
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qqktbb4p-1634629290160)(/Users/lewis/Library/Application Support/typora-user-images/image-20211018153908473.png)]

ReentronLock 在多层可重入锁调用时,lock数必须与unlock数对等,否则会出现死锁

三、可重入锁简单实现

/**
 * MyRelock
 * Description 自定义实现重入锁简单实现,只完成了 lock和unlock
 * Create by wangzx
 * Date 2021年10月18日 15:43:00
 */
public class MyRelock implements Lock {

    //是否已经上锁
    private boolean isLocked = false;

    //记录当前线程
    Thread currThread = null;

    //记录当前锁数量
    private int lockNum = 0;

    @Override
    public void lock() {
        //获取当前线程
        Thread thread = Thread.currentThread();
        //当前已上锁 且 当前线程不是已上锁线程 进入循环等待
        while (isLocked && thread!= currThread ){
            try {
                Thread.sleep(50);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }

        // 锁状态置为true
        isLocked = true;
        // 记录当前线程
        currThread = thread;
        // 锁记录数 加一
        lockNum++;
    }


    @Override
    public void unlock() {
        // 判断是否为当前锁的线程
        if(Thread.currentThread()==currThread){
            // 锁记录 --
            lockNum--;
            if (lockNum == 0){
                isLocked = false;
            }
        }
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }
    @Override
    public boolean tryLock() {
        return false;
    }
    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }
    @Override
    public Condition newCondition() {
        return null;
    }

测试

MyRelock myRelock = new MyRelock();
    private static void testMyRelock(){
        Demo demo = new Demo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                demo.functionA();
            }
        }).start();
    }

    private void functionA(){
        myRelock.lock();
        System.out.println("functionA 执行了,锁为:"+myRelock);
        functionB();
        myRelock.unlock();
    }

    private void functionB(){
        myRelock.lock();
        System.out.println("functionB 执行了,锁为:"+myRelock);
        myRelock.unlock();
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-clZjih1v-1634629290161)(/Users/lewis/Library/Application Support/typora-user-images/image-20211018163405154.png)]

四、synchronized可重入实现原理

每一个锁关联一个线程持有者和一个计数器,当计数器为0时表示当前锁没有被任何线程持有,此时任何线程都可以获取锁并执行相应的代码;当某一个线程获取锁成功后,jvm就会记录当前次有锁的线程,并将计数器加1;此时其他线程请求当前锁,则需要等待,如果持有当前锁的线程再次请求当前锁,可以获得当前锁,并且计数器加1,当锁内容执行完成后,计数器减1,如果当前计数器为0时,则线程持有者置为null,此时释放锁,唤醒等待线程。

五 、ReentronLock 可重入实现

// 同步队列,是一个带头结点的双向链表,用于实现锁的语义
public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
  // 重入锁计数/许可证数量,在不同的锁中,使用方式有所不同
    private volatile int state;
  
 //CAS方式更新状态
  protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }
}


public class ReentrantLock implements Lock, java.io.Serializable {
  /*
     * 同步队列的实现者,实现了锁的语义
     *
     * 许可证数量==0:当前锁空闲
     * 许可证数量>0,当前锁被某一线程持有
     * 一个线程多次进入锁时,许可证数量会递增
     * 当线程释放锁时,许可证数量减少,直到为0
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * Performs {@link Lock#lock}. The main reason for subclassing
         * is to allow fast path for nonfair version.
         */
        abstract void lock();

        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        // 申请一次非公平锁,返回值代表锁是否申请成功
        final boolean nonfairTryAcquire(int acquires) {
            // 获取生成锁的线程
            final Thread current = Thread.currentThread();
            // 当前许可证数量  当前锁状态
            int c = getState();
            // 如果锁没有被任何线程占用
            if (c == 0) {
                /* 发现锁空闲时,不管【同步队列】中有没有人抢锁,该线程直接尝试抢锁,这也是"非公平"所在 */

                // 尝试更新许可证数量为acquires,返回true代表更新成功,即成功抢到了锁
                if (compareAndSetState(0, acquires)) {
                    // 设置当前线程为<占有者线程>
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            /* 至此,说明锁已被占用 */
            // 如果当前线程不是锁的占用者,直接返回false,代表抢锁失败,该线程需要去排队
            else if (current == getExclusiveOwnerThread()) {
                /* 至此,说明当前线程就是占用锁的线程,则需要再次持有锁,累计许可证数量,这也是"可重入"的含义 */
                int nextc = c + acquires;
                // 更新许可证数量
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
        // 释放一次锁,返回值表示同步锁是否处于自由状态(无线程持有)
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
    }
}

上面是ReentronLock实现的部分代码## 一、重入锁理解

  • 某线程获取锁后,可以再次获取锁
  • synchronized 和 ReentrantLock 都是可重入锁
  • 可重入锁解决同一线程中内外层方法调用死锁的问题

二、可重入锁示例

1、synchronized 可重入锁演示

    /**
     * synchronized 重入锁示例
     */
    private static void reLockSyncDemo(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (this){
                    System.out.println("第1次获取锁,当前锁是:" + this);
                    int index=0;
                    while(true){
                        synchronized (this){
                            System.out.println("第" + (++index) + "次获取重入锁,当前锁是:" + this);
                        }
                        if (index == 5) {
                            break;
                        }
                    }
                }
            }
        }).start();
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mceqrTCf-1634629291378)(/Users/lewis/Library/Application Support/typora-user-images/image-20211018153755361.png)]

2、ReentronLock 可重入锁演示

 		/**
     * ReentrantLock 重入锁示例
     */
    private static void reentrantLockDemo(){
        ReentrantLock rl = new ReentrantLock();
        new Thread(new Runnable() {
            @Override
            public void run() {
                rl.lock();
                try {
                    System.out.println("第1次获取锁,当前锁是:" + rl);
                    int index = 0;
                    while (true) {
                        rl.lock();
                        try {
                            System.out.println("第" + (++index) + "次获取重入锁,当前锁是:" + rl);
                            if (index == 5) {
                                break;
                            }
                        } finally {
                            rl.unlock();
                        }
                    }
                }finally{
                    rl.unlock();
                }
            }
        }).start();
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NeCrmrZm-1634629291379)(/Users/lewis/Library/Application Support/typora-user-images/image-20211018153908473.png)]

ReentronLock 在多层可重入锁调用时,lock数必须与unlock数对等,否则会出现死锁

三、可重入锁简单实现

/**
 * MyRelock
 * Description 自定义实现重入锁简单实现,只完成了 lock和unlock
 * Create by wangzx
 * Date 2021年10月18日 15:43:00
 */
public class MyRelock implements Lock {

    //是否已经上锁
    private boolean isLocked = false;

    //记录当前线程
    Thread currThread = null;

    //记录当前锁数量
    private int lockNum = 0;

    @Override
    public void lock() {
        //获取当前线程
        Thread thread = Thread.currentThread();
        //当前已上锁 且 当前线程不是已上锁线程 进入循环等待
        while (isLocked && thread!= currThread ){
            try {
                Thread.sleep(50);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }

        // 锁状态置为true
        isLocked = true;
        // 记录当前线程
        currThread = thread;
        // 锁记录数 加一
        lockNum++;
    }


    @Override
    public void unlock() {
        // 判断是否为当前锁的线程
        if(Thread.currentThread()==currThread){
            // 锁记录 --
            lockNum--;
            if (lockNum == 0){
                isLocked = false;
            }
        }
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }
    @Override
    public boolean tryLock() {
        return false;
    }
    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }
    @Override
    public Condition newCondition() {
        return null;
    }

测试

MyRelock myRelock = new MyRelock();
    private static void testMyRelock(){
        Demo demo = new Demo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                demo.functionA();
            }
        }).start();
    }

    private void functionA(){
        myRelock.lock();
        System.out.println("functionA 执行了,锁为:"+myRelock);
        functionB();
        myRelock.unlock();
    }

    private void functionB(){
        myRelock.lock();
        System.out.println("functionB 执行了,锁为:"+myRelock);
        myRelock.unlock();
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-24wCcr7q-1634629291380)(/Users/lewis/Library/Application Support/typora-user-images/image-20211018163405154.png)]

四、synchronized可重入实现原理

每一个锁关联一个线程持有者和一个计数器,当计数器为0时表示当前锁没有被任何线程持有,此时任何线程都可以获取锁并执行相应的代码;当某一个线程获取锁成功后,jvm就会记录当前次有锁的线程,并将计数器加1;此时其他线程请求当前锁,则需要等待,如果持有当前锁的线程再次请求当前锁,可以获得当前锁,并且计数器加1,当锁内容执行完成后,计数器减1,如果当前计数器为0时,则线程持有者置为null,此时释放锁,唤醒等待线程。

五 、ReentronLock 可重入实现

// 同步队列,是一个带头结点的双向链表,用于实现锁的语义
public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
  // 重入锁计数/许可证数量,在不同的锁中,使用方式有所不同
    private volatile int state;
  
 //CAS方式更新状态
  protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }
}


public class ReentrantLock implements Lock, java.io.Serializable {
  /*
     * 同步队列的实现者,实现了锁的语义
     *
     * 许可证数量==0:当前锁空闲
     * 许可证数量>0,当前锁被某一线程持有
     * 一个线程多次进入锁时,许可证数量会递增
     * 当线程释放锁时,许可证数量减少,直到为0
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * Performs {@link Lock#lock}. The main reason for subclassing
         * is to allow fast path for nonfair version.
         */
        abstract void lock();

        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        // 申请一次非公平锁,返回值代表锁是否申请成功
        final boolean nonfairTryAcquire(int acquires) {
            // 获取生成锁的线程
            final Thread current = Thread.currentThread();
            // 当前许可证数量  当前锁状态
            int c = getState();
            // 如果锁没有被任何线程占用
            if (c == 0) {
                /* 发现锁空闲时,不管【同步队列】中有没有人抢锁,该线程直接尝试抢锁,这也是"非公平"所在 */

                // 尝试更新许可证数量为acquires,返回true代表更新成功,即成功抢到了锁
                if (compareAndSetState(0, acquires)) {
                    // 设置当前线程为<占有者线程>
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            /* 至此,说明锁已被占用 */
            // 如果当前线程不是锁的占用者,直接返回false,代表抢锁失败,该线程需要去排队
            else if (current == getExclusiveOwnerThread()) {
                /* 至此,说明当前线程就是占用锁的线程,则需要再次持有锁,累计许可证数量,这也是"可重入"的含义 */
                int nextc = c + acquires;
                // 更新许可证数量
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
        // 释放一次锁,返回值表示同步锁是否处于自由状态(无线程持有)
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
    }
}

上面是ReentronLock实现的部分代码

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值