自己实现可重入锁

什么是可重入锁

假设现在有一个线程A 他获取了锁,然后他再一次去尝试获取锁,如果能成功,就说这个锁是可以重入的,在java中 ReentrantLock.class就是一个可重入的锁,关于这个类的源码解析,将在后面的章节讲到,现在我们看一下一个可重入锁的基本使用。

使用ReentrantLock

首先我们写一个简单的例子,这个例子是线程不安全的:

public class NoteSafe {
   private static Integer n = 5;
   public static void main(String[] args) {
       //新建一个线程池
       ExecutorService exe = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
      //在线程池中启动 50个线程
       for (int i = 0; i < 50; i++) {
           exe.execute(() -> {
               try {
                   if (n > 0) {
                       Thread.sleep(500);
                       n--;
                   }
               } catch (Exception e) {
                   e.printStackTrace();
               }
           });
       }
       exe.shutdown();
       while (!exe.isTerminated()){
       }
       System.out.println(n);
   }
}

上面的程序的逻辑就是 ,我们设置了一个参数 n,然后同时启动50个线程去并发访问这个n,去执行 -1 的操作。
最后查看结果,当然结果肯定就是 一个负数了。

现在我们使用可重入锁来改造这个不安全的类
通过一个创建一个ReentrantLock ,调用他的lock()方法,将资源上锁,使用unlock()方法进行解锁。

public class NoteSafe {
    private static Integer n = 5;
    public static void main(String[] args) {
        ExecutorService exe = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
        ReentrantLock reentrantLock = new ReentrantLock();
        for (int i = 0; i < 50; i++) {
            exe.execute(() -> {
                try {
                    reentrantLock.lock();
                    if (n > 0) {
                        Thread.sleep(500);
                        n--;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    reentrantLock.unlock();
                }
            });
        }
        exe.shutdown();
        while (!exe.isTerminated()){
        }
        System.out.println(n);
    }
}

这里我们创建了一个 可重入的锁,然后利用lock 锁住,运行的结果一定是0了
可以看到和synchronized 不一样的是 ReentrantLock 需要我们自己手动的上锁和解锁,斜体样式所以在使用的时候应该把解锁也就是unlock放入到finally 的代码块里去,以免出现死锁这点很重要

自己去实现一个可重入锁

通过上面的这个例子我们已经掌握了,可重入锁的使用方法,他的特点就是手动的上锁和解锁。
下面我们将自己去实现一个可重入的锁。

实现Lock 接口

首先我们看一下java中有哪些类实现了这个接口。
在这里插入图片描述
可以看到 我们一些常见的可重入的锁,读写锁还有我们上面用到的ReentrantLock 都实现了这个接口。那么我们也只要去实现这个接口就行。
然后去重写 他的lock()unlock() 方法。

具体代码实现

public class DemoReentryLock implements Lock {
    /**
     * 用来标记是否已经有线程获取了锁
     * 是的话 值为true 否则为 false
     */
    private boolean isHold = false;
    /**
     * 用来记录当前获取锁的线程
     */
    private Thread nowHoldThread = null;
    /**
     * 记录锁重入的次数
     */
    private int holdNum = 0;

    @Override
    public synchronized void lock() {
        /**
         * 判断是否已经有线程获取了锁,
         * 如果已经有人获取了锁,再判断当前操作锁的线程是不是获取了锁的线程
         */
        if (isHold && Thread.currentThread() != nowHoldThread) {
            //说明当前操作锁的线程,不是获取锁的线程,将当前线程挂起
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //没有人获取锁,或者被挂起的线程被唤醒了,将获取锁的线程,变成当前操作锁的线程
        nowHoldThread = Thread.currentThread();
        //将重入的次数加一,同时设置 标志位位true,表示锁已经被获取
        holdNum++;
        isHold = true;
    }

    @Override
    public synchronized void unlock() {
        //解锁的时候,首先判断当前操作的线程是不是获取了锁的线程
        if (Thread.currentThread() == nowHoldThread) {
            //将记录锁重入的次数 -1 ,在判断其值是不是0,是0说明重入的次数已经全部出来了,就可以通知其他线程来获取锁
            holdNum--;
            if (holdNum == 0) {
                isHold = false;
                notify();
            }
        }
    }
  //这里省略了其他要重写的方法,为了节省空间
}

将我们自己实现的锁去替代上一个例子中的,ReentrantLock ,重新运行程序,结果一直是0 ,说明这个锁已经生效

验证我们写的锁是否能重入

到上面为止,我们已经成功实现了一个锁,至于他是不是可重入的,还需要程序验证一下,也很简单
我们只要新建一个线程,在里面上锁后,不要解锁直接再上锁一次,看看会不会报错就可以

public class ReentDemo {
    public static void main(String[] args) {
        DemoReentryLock demoReentryLock = new DemoReentryLock();
        new Thread(() -> {
            System.out.println("我开始上锁了");
            demoReentryLock.lock();
            demoReentryLock.lock();
            demoReentryLock.unlock();
            demoReentryLock.unlock();
            System.out.println("已经全部解锁");
        }).start();

    }
}

运行后程序正常输出,说明我们的锁是可以重入的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值