java中lock的学习笔记(一)ReentrantLock

前言:

     对于java的锁机制,可以说是长久以来一直困扰着我,经过长久的挣扎,终于决定来学习下,下面就就记录下日常学习的lock的笔记,以记录的形式来督促自己学习,毕竟有惰性,也不是学习天才,只能不断的鞭策自己,让自己学习。

说到java的锁,立刻就想到sychronized和Lock,但是让说一下java的锁,却又不知如何说起。。。

相比于Lock来说sychronized关键字不太好理解,我们先来看看Lock,这个是java写的我们作为一个码农,看代码应该更容易理解,就来看Lock的代码。首先上一段使用场景的代码

上图就主线程开了两个子线程,子线程run方法打印一句话,图1未使用lock,两个线程都打印了内容,而图2使用了lock,只有一个线程打印了内容,这有两个子线程都传入了lock这个实例化对象。这里我们就好理解这个锁了,就可以理解成,你只有先获取锁才能执行之后的操作,例如一个苹果两个人抢,谁抢到了谁就可以吃,吃就是之后的动作,苹果就是锁,是一个道理。

那么锁是如何实现的呢?来看看锁的实现原理,作为程序员,当然是以代码说话了

先看锁Lock的一个重要实现类ReentrantLock

一进入 ReentrantLock类就看到了三个内部类Sync、NonfairSync、FairSync,同步、非公平同步、公平同步,其实就是对应的我们公平锁和非公平锁的实现类,而Sync抽象类,将它们的共性进行抽取,形成了一个基类。

下来在来看看加锁的具体实现(Sync类)继承自AbstractQueuedSynchronizer类(抽象同步队列),AbstractQueuedSynchronizer又继承自AbstractOwnableSynchronizer

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {

 可以看下AbstractOwnableSynchronizer代码如下,就一个属性exclusiveOwnerThread独占线程,也就是说这个抽象类就是记录占有该锁的线程的。

public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {

    /** Use serial ID even though all fields transient. */
    private static final long serialVersionUID = 3737899427754241961L;

    /**
     * Empty constructor for use by subclasses.
     */
    protected AbstractOwnableSynchronizer() { }

    /**
     * The current owner of exclusive mode synchronization.
     */
    private transient Thread exclusiveOwnerThread;

    /**
     * Sets the thread that currently owns exclusive access.
     * A {@code null} argument indicates that no thread owns access.
     * This method does not otherwise impose any synchronization or
     * {@code volatile} field accesses.
     * @param thread the owner thread
     */
    protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }

    /**
     * Returns the thread last set by {@code setExclusiveOwnerThread},
     * or {@code null} if never set.  This method does not otherwise
     * impose any synchronization or {@code volatile} field accesses.
     * @return the owner thread
     */
    protected final Thread getExclusiveOwnerThread() {
        return exclusiveOwnerThread;
    }
}

看到了记录占有锁的线程记录的类以及属性,那锁有没有被占用的标识在哪里呢?是在AbstractQueuedSynchronizer类中的state属性,类型为int。

搞清楚了两个重要标识,就来看看基础类sync类中的8个方法,一看便知如下。

看一个加锁的方法,非公平锁这个方法是怎么加锁的,还是来看源码

/**
* 非公平的尝试获取锁(因为一进来就直接先去加锁,而不是去队列等待,所以不公平)
*/
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    //判断锁的状态未被占用,则直接cas加锁(修改状态值)并设置独占线程拥有者为当前线程
    if (c == 0) {
       if (compareAndSetState(0, acquires)) {
          setExclusiveOwnerThread(current);
          return true;
       }
//如果状态是占用的,判断占用的线程是否是当前线程,是当前线程,则状态值加上相应的数字,这就是可重入的原理
    } else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
           throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return
}

看了这个类,可以知道锁的基本原理以及可重入的原理了,就是修改一个对象中的状态0为未锁定,其余大于0的状态为锁定状态,锁定后,并记录锁定的线程,当该线程再次来获取这个锁的时候,就对状态值+1.

看到了非公平锁的获取方法,再来看下公平锁的获取方法

 // 公平的同步对象,公平锁使用的类
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                // 锁是未锁定状态时,先判断否有等待的队列,没有则cas加锁
                if (!hasQueuedPredecessors() &&
                        compareAndSetState(0, acquires)) {
                    // 设定占有锁的线程为当前线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
                // 重入锁的处理(是重入,则状态值+1)
            } else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

公平同步对象的中只有两个方法,一个是实现的基类Sync中的lock方法,调用了父类AbstractQueuedSynchronizer中的acquire方法,而acquire的方法中调用的tryAcquire方法就是上面的tryAcquire方法。

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

在lock中还有一个队列的概念,这就是下一个学习的目标,弄清楚这个队列的属性,以及怎么使用的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值