浅谈Sychronized和ReetrantLock

	在我们的日常开发中,我们很多时候会或多或少的遇到一些并发场景,比如在某个订单模块或者秒杀块,多个用
户在同一时间内点击抢单和下单,后台是如何处理线程安全问题呢?底层实现原理又是什么?
	我们常规的处理方法可能是使用Sychronized和ReentrantLock进行处理,在资源竞争相对不是很激烈的时候
Synchronized同步方式要比ReentrantLock加锁的方式性能要好,随着并发量增大,竞争越来越激烈,Synchronized
性能下降很快,而ReentrantLock基本保持不变。所以ReetrentLock的lock()和unlock()方式比较适用于一些并发
量比较大的场景。
	我们如果深入研究Synchronized和ReetrentLock的底层实现我们会发现,Synchronized是通过底层汇编语言实现同步的也就是monitor监视器,配合synchronized使用的wait()和notify()其本质上也是通过汇编monitor监视器实现,我们使用的时候可以javap -c SynchronizedDemo类 查看底层汇编执行顺序。在这里不在过多赘述。上图。

浅谈Sychronized和ReetrantLock_第1张图片

	ReentrantLock深入底层源码实现我们可以看到和一个AbstractQueuedSynchronizer(AQS)还有一个抽象的静态内部类Sync,抽象静态内部类中有一些抽象方法。

浅谈Sychronized和ReetrantLock_第2张图片
其实AQS也是一个抽象类继承了AbstractOwnableSynchronizer这个类,在该类中有一个用于保存当前那个线程获得了锁的一个变量exclusiveOwerThread该变量记录了当前获得锁的线程。
浅谈Sychronized和ReetrantLock_第3张图片

我们返回AQS类中有三个比较重要的变量 分别为:
1.private transient volatile Node head; 线程CLH队列的队列头指针
2. private transient volatile Node tail; 线程CLH队列的队列的尾指针
3.private transient volatile Node tail; 当前线程的状态(0 为没有线程占有锁,1为有线程占有锁)

当我们使用lock()方法上锁的时候,会经过一下步骤。(以公平锁为例子:可以通过Reetrant(boolean flag)传入一个布尔值创建公平锁还是非公锁,默认为非公平锁)

//FariSync为公平锁类集成sync 静态的抽象内部类实现他的抽线lock()方法,
static final class FairSync extends Sync {
     
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
     
            acquire(1);//尝试获得锁,将当前锁的状态改为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) {
     
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
     
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
     
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
 上述代码中的调用acquire()其实调用的是我们AQS类中的方法如下,当调用tryAcquire()方法转到我们FairSync中重写父类AQS的方法。

浅谈Sychronized和ReetrantLock_第4张图片

在ReetrantLock中的静态内部类中我们可以找到该方法,(公平锁和非公平锁实现有些差别)。从如下代码中可以看处
(1)首先获得当前线程
(2)获得线程状态
(3)如果当前队列中没有等待获得锁的线程并且线程锁的状态为0,那么我们使用CAS同步方法将锁的状态改为1,表示当前线程持有锁
(4)否则的话,如果是当前线程正在持有锁,那么将锁定的状态加上传入的acquires的值,并设置状态后返回true,表示获得锁成功
(5)以上是我在读源码中的理解,如果有不对的地方,欢迎各位指正。互相学习。

浅谈Sychronized和ReetrantLock_第5张图片

以上是锁的获得过程,理清楚ReetrantLock 中的 变量Sync 和 AQS类的关系 以及FairSync和NonFairSync和AQS类的关系,以及上锁和解锁过程中的流程和各个变量的作用,应该就很清楚了,以下是unlock()方法实现,用来释放对锁的占用。

/**
     * Attempts to release this lock.
     *
     * 

If the current thread is the holder of this lock then the hold * count is decremented. If the hold count is now zero then the lock * is released. If the current thread is not the holder of this * lock then {@link IllegalMonitorStateException} is thrown. * * @throws IllegalMonitorStateException if the current thread does not * hold this lock */ **//这是我们 ReetrantLock中的unlock方法 用来释放对锁的占用** public void unlock() { sync.release(1); } 以下是AQS类中的方法============================================================ /** * Wakes up node's successor, if one exists. * * @param node the node */ private void unparkSuccessor(Node node) { /* * If status is negative (i.e., possibly needing signal) try * to clear in anticipation of signalling. It is OK if this * fails or if status is changed by waiting thread. */ int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); /* * Thread to unpark is held in successor, which is normally * just the next node. But if cancelled or apparently null, * traverse backwards from tail to find the actual * non-cancelled successor. */ Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null) LockSupport.unpark(s.thread); }

释放对锁定的占用,将当前线程从队列中删除,并且将当前持有锁的状态改为0;

以上是我对Synchronized和ReetrantLock的一些个人理解,以及学习过程中,查阅资料的总结,如果错误,请大家指出,互相学习,共同提高!!!

你可能感兴趣的:(Synchronized,ReetrantLock)