ReentrantLock和Synchorized的区别

上次通过ReentrantLock来解析AQS,这次我们来详细讲讲ReentrantLock和Synchorized的异同吧。

大多数人在需要将一个方法变成线程安全时,第一个想到的就是加上Synchorized关键字,确实通过加Synchorized的方式是很简单的,开发者不需要再关心锁的释放,程序跑就行了,但正是这么简单的方式会带来很多的问题,记住,越是简单的东西,其带来的扩展性是很低的,小伙伴们要慎用哦,接下来就分别来看看ReentrantLock和Synchorized的使用方式和优劣吧。

首先我们先来比较一下两个的异同。

1.内部实现锁的方式

下图为Synchornized的锁升级机制,图来自于网络,可以了解一下

在这里插入图片描述

Synchorized: 加锁是通过底层的JVM对象指令,争夺的是moniter对象,通过对象头的信息来判断改对象是否加锁并且线程持有的threadId,同时在Synchorized加锁过程中,多个线程争抢时还会发生锁的升级机制,大概过程为:无锁–>偏向锁–>轻量级锁–>重量级锁

ReentrantLock:这个加锁过程在上篇文章中已经将的很清楚了,通过cas机制来判断锁是否有线程持有,如果没有就将state由0改为1并设置锁的持有线程为当前线程,同时还有可重入机制。

2.锁的释放

Synchorized:用户不可以手动去释放,只有加锁的代码块运行完成或者出现异常,锁才会自动释放,这样多个线程并发会造成死锁问题。关于死锁,后续我也会详细讲解。

ReentrantLock:通过lock()手动加锁,unLock()手动释放锁,方式更加灵活。

3.锁是否可以中断

Synchorized:不可中端锁,除非加锁代码块发生异常或者运行成功。

ReentrantLock:在上篇文中只讲解了lock()方法,在ReentrantLock中有tryLock()方法,可以传入获取锁的时间,当超过传入时间时,会将线程挂起加入等待队列,等待再次被唤醒。

4.是否是公平锁

Synchorized:非公平锁,多个线程都有获取到锁的机会,不区分线程的先来后到。

ReentrantLock:这个上篇也讲到了,在ReentrantLock默认不带参数初始化时,是创建的非公平锁,new ReentrantLock(boolean fair) 可以用来指定新建锁是否是公平的。

tips: 关于上篇中,有个思考,ReentrantLock怎么来实现公平锁和公平锁的,这里就来解答啦,直接上源码就很清晰了,go

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * Performs lock.  Try immediate barge, backing up to normal
     * acquire on failure.
     */
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }
}

 static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

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

区别就在于NonfairSync()(非公平)中多了个compareAndSetState(0, 1)方法,这个很关键,在非公平的时候,有这样一种情况,当锁释放之后,这是有新创建的线程也来正争抢锁,这是和等待队列的线程一起发生的,这个时候就破坏了先来后到的机制了。在公平锁的时候,每次新建线程都要当前线程等待获取锁,所以后来的锁一定是后获取到锁的。

注意:在等待队列里面的线程,是一定遵循先来的先获取到锁,后来的后获取到到锁,不管是公平锁还是非公平锁都是如此,上面是的不遵循的情况指的是当前锁还没有进入到等待队列的情况,它会和等待队列里面的线程一起争抢锁哦。 其实上面也解释了我上篇文章中的疑惑,为什么state==0这个状态要重复判断两次的问题。

5.锁是否可以绑定条件Condition

Synchorized:锁的睡眠和唤醒是通过在有Synchorized代码块中的notify()和wait()方法来实现的,这个唤醒是随机的并不能精准唤醒某一个线程。

ReentrantLock:通过用创建的锁来绑定不同的Condition对象,可以达到精准唤醒的目的,具体方法为await()和signal()方法。

6.锁的目标

Synchorized:锁的是对象,通过对象头中的锁的表示来判断当前是否有线程获取到锁。

ReentrantLock:锁的是线程,通过源码可以看到,通过判断进入的线程和当前锁设置的线程,已经state的值来说明当前线程能否获取到锁。

借鉴

以上就是ReentrantLock和Synchorized的区别了,小伙伴们可以在日常开发中,合适的选择使用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值