浅谈Sychronized和ReetrantLock

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

在这里插入图片描述

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

在这里插入图片描述
其实AQS也是一个抽象类继承了AbstractOwnableSynchronizer这个类,在该类中有一个用于保存当前那个线程获得了锁的一个变量exclusiveOwerThread该变量记录了当前获得锁的线程。
在这里插入图片描述

我们返回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的方法。

在这里插入图片描述

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

在这里插入图片描述

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

/**
     * Attempts to release this lock.
     *
     * <p>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的一些个人理解,以及学习过程中,查阅资料的总结,如果错误,请大家指出,互相学习,共同提高!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: synchronized和reentrantlock都是Java中用于实现线程同步的机制。 synchronized是Java中最基本的同步机制,它可以用于方法或代码块级别的同步。当一个线程进入synchronized代码块时,它会尝试获取锁,如果锁已经被其他线程持有,则该线程会被阻塞,直到锁被释放。synchronized机制是Java中最常用的同步机制之一,但它有一些限制,例如无法中断正在等待锁的线程,也无法尝试获取锁而不阻塞。 reentrantlock是Java中另一种同步机制,它提供了更多的灵活性和控制。与synchronized不同,reentrantlock可以中断正在等待锁的线程,也可以尝试获取锁而不阻塞。此外,reentrantlock还提供了一些高级功能,例如公平锁和可重入锁。但是,reentrantlock的使用比synchronized更复杂,需要手动获取和释放锁,并且需要注意避免死锁等问题。 总的来说,synchronized是Java中最基本的同步机制,适用于大多数情况。而reentrantlock则提供了更多的灵活性和控制,适用于一些特殊的场景。 ### 回答2: synchronized和ReentrantLock是Java中用于实现线程同步的两种机制。 synchronized是Java中最基本的线程同步机制,它使用了内置的监视器锁(也称为对象锁或互斥锁)来控制线程的访问。synchronized关键字可以用于方法或代码块,并且是隐式锁。当线程执行到synchronized关键字时,会自动获取锁,执行完后会自动释放锁。synchronized关键字确保了在同一时间只有一个线程可以访问被锁定的代码或方法,从而保证了线程的安全性。然而,synchronized关键字也有一些缺点,比如无法获得锁时会一直阻塞等待和释放锁。 ReentrantLock是Java中的另一种线程同步机制,它是通过显示锁来实现的。ReentrantLock提供了更灵活的锁定操作,相比于synchronized,它具有更多的特性。ReentrantLock可以使用lock()和unlock()方法来手动加锁和解锁,这意味着我们可以更加精确地控制代码的同步性。ReentrantLock还支持可重入性,即线程可以多次获得同一个锁,提供了更高的灵活性。它还支持公平锁和非公平锁两种模式,可以按照先进先出的规则来选择获取锁的线程。此外,ReentrantLock还提供了一些高级功能,比如可中断锁、超时锁和条件变量等。 总的来说,synchronized是Java中最基本的线程同步机制,使用简单但灵活性较低。而ReentrantLock则提供了更多的功能和灵活性,但使用相对复杂一些。在实际应用中,我们可以根据具体需求来选择使用哪种线程同步机制。 ### 回答3: synchronized和ReentrantLock都是Java中用于实现线程同步的工具。 synchronized是Java中最常见的同步关键字,它可以应用在方法或代码块上。当一个线程获取到对象的synchronized锁时,其他线程就无法同时访问这个对象。当该线程执行完代码块或者方法,会释放锁,其他线程才能获得锁并执行。synchronized是隐式获取和释放锁的过程,非常方便,但也存在一些限制,例如无法中断一个正在等待获取锁的线程。 ReentrantLock是Java中提供的一种显示锁,可以实现与synchronized类似的功能。与synchronized不同的是,ReentrantLock提供了更灵活和更强大的功能。它允许更细粒度的控制锁定的获取和释放,例如通过lock()和unlock()方法手动获取和释放锁,也可以通过tryLock()方法尝试获取锁而不需要一直等待。此外,ReentrantLock还支持公平性,即按照线程请求的顺序获取锁,避免产生线程饥饿现象。而synchronized则不保证公平性。 另外,ReentrantLock提供了Condition接口,可以使用newCondition()方法创建多个条件变量。条件变量可以用于线程间的通信,可以让某个线程等待特定条件满足后再继续执行,或者通知其他等待线程某个条件已经满足。 总的来说,synchronized是Java中隐式锁机制的实现,使用方便但功能有限;而ReentrantLock是显示锁机制的实现,提供了更多灵活和强大的功能,但使用起来需要更细致的控制。根据具体情况需要,我们可以选择合适的同步机制来保证线程安全。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值