手撸JDK之ReentrantLock锁那点事

前置思想

首先明确一点ReentrantLock与synchronized最大的不同点在于synchronized是锁住了对象的头部,而ReentrantLock是类似于我们买一把锁,锁住我们不想因为多线程下导致资源的原子性 ,可见性的问题,在明确这一点时我们的锁其实与生活中的所没太大区别,生活中的锁无非就是大门挂一把锁,这样我们就告诉别人此处以上锁,唯一不同的是程序锁必须由上锁的人来解锁,而生活中我们可以持有别人的锁进行开锁。
在这里插入图片描述

主要使用到的类

  • AtomicInteger:主要保证此锁的原子性与可见性,以及涉及到后面的可重入锁的状态,注:volatile只能保证资源的可见性,并不能保证原子性
  • Thread:当前那个线程持有这把锁【PS:也就是谁持有了锁,此处到后面可重入锁,以及解锁所需要的】
  • LinkedBlockingQueue:没抢到锁对象的容器
  • LockSupport:切换线程状态

交代完了开怼!!!!

/**
     * 线程状态定义
     */
    AtomicInteger state = new AtomicInteger();

    /**
     * 那个线程持有锁
     */
    Thread ownerThread = null;

    /**
     * 存放线程的容器
     */
    LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue<>();

这步没啥可说的继续!!

public boolean tryLock() {
    
    
        //不用每次都进行CAS操作,如果当期状态为0我直接返回抢锁成功
        if (state.get() == 0) {
    
    
            //CAS抢锁
            if (state.compareAndSet(0, 1)) {
    
    
                //将抢到锁的线程设定为当前锁的持有者
                ownerThread = Thread.currentThread();
                return true;
            }
        } else if (ownerThread == Thread.currentThread()) {
    
    
            System.out.println("重入锁成功!!!!");
            state.set(state.get() + 1);
            return true;
        }
        return false;
    }

此处主要用到了CAS机制,如果修改成功证明已抢到锁,失败则加入队列,以及重入锁如果是当前线程那么获取锁成功,并且在状态+1【PS:此处状态+1主要是为了可重入锁,应为重入锁的本质需要你加锁几次就要释放几次】

public void lock() {
    
    
        //真公平锁  如果没有!waiters.isEmpty(),在锁的上一个持有者他也会抢
        if (!waiters.isEmpty() || !tryLock()) {
    
    
            //没抢到锁进入等待队列
            waiters.add(Thread.currentThread());
            for (; ; ) {
    
    
                if (tryLock()) {
    
    
                    //移除并且返回队列头部
                    waiters.poll();
                    return;
                } else {
    
    
                    //没抢到   进入阻塞(WAITING)
                    LockSupport.park();
                }
            }
        }
    }

如果当前没有线程持有锁则直接返回,有则将他加入队列并且阻塞【PS:当然一些猿认为使用LinkedBlockingQueue不就已经是公平锁了么(先入先出),其实不然比如:有三个线程,1抢锁成功,在释放后其实1也参与抢锁了,也就是插队!!此处的插队就导致看似公平的锁变的不公平了】

public void unlock() {
    
    
        //如果不是当前线程那么不允许释放锁
        if (Thread.currentThread() != ownerThread) {
    
    
            throw new RuntimeException("不是你的别乱动,你不是锁的持有者");
        }
        //释放锁的过程,每次过来减一
        if (state.decrementAndGet() == 0) {
    
    
            ownerThread = null;
            //获取第一个等待的元素并且通知他开始抢锁了,此时还不能移除
            //在真正抢到锁后才能移除队列
            Thread waiter = waiters.peek();
            if (waiter != null) {
    
    
                LockSupport.unpark(waiter);
            }
        }
    }

此处我们释放锁的时候必须是当前持有锁的线程,如果其他线程来释放锁呢就乱套了
PS:如有不对的地方请大手不吝啬指出

猜你喜欢

转载自blog.csdn.net/cj181jie/article/details/108710092